/* X Communication module for terminals which understand the X protocol.
   Copyright (C) 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs 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.

GNU Emacs 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 GNU Emacs; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "config.h"

#include <stdio.h>
#include <sys/time.h>

#include <X11/Xlib.h>
#ifdef HAVE_XMU
#include <X11/Xmu/Error.h>
#else
#include "xmu.h"
#endif

#include "xintrinsic.h"
#include <X11/StringDefs.h>

#include <X11/Shell.h>
#ifdef EXTERNAL_WIDGET
#include "ExternalShell.h"
#endif
#include "EmacsShell.h"
#include "EmacsScreen.h"

#include "lisp.h"
#include "intl.h"
#include "hash.h"
#include "window.h"
#include "dispextern.h"
#include "dispmisc.h"
#include "faces.h"
#include "elhash.h"

#ifdef USG
#include <sys/utsname.h>
#endif

#include "xgccache.h"

extern XtAppContext Xt_app_con;
extern Widget Xt_app_shell;

#ifdef HAVE_X_WINDOWS

/* On 4.3 this loses if it comes after xterm.h.  */
/* #include <signal.h>  use "syssignal.h" instead -jwz */
#include "syssignal.h"

/* This may include sys/types.h, and that somehow loses
   if this is not done before the other system files.  */
#include "xterm.h"
#include "xobjs.h"

#include "lwlib.h"

/* Load sys/types.h if not already loaded.
   In some systems loading it twice is suicidal.  */
#ifndef makedev
#include <sys/types.h>
#endif

#ifdef O_NDELAY
#undef O_NDELAY
#endif

#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <setjmp.h>
#ifdef BSD
#include <sys/ioctl.h>
#include <strings.h>
#else
#if	!defined(HPUX) && !defined(LINUX)
/* Linux added here by Raymond L. Toy <toy@alydar.crd.ge.com> for XEmacs. */
#include <sys/termio.h>
#endif /* HPUX */
#include <string.h>
#endif
#include <sys/stat.h>
#include <sys/param.h>

#include "termhooks.h"
#include "termopts.h"
#include "termchar.h"
#include "buffer.h"
#include "screen.h"
#include "disptab.h"
#include "bitmaps.h"

#include "events.h"

#include "sysdep.h"             /* old_fcntl_owner */

static String x_fallback_resources[] = {
  /* This file is automatically generated from the app-defaults file
     in ../etc/Emacs.ad.  These resources are consulted only if no
     app-defaults file is found at all.
   */
# include "Emacs.ad.h"
  0 };

#define min(a,b) ((a)<(b) ? (a) : (b))
#define max(a,b) ((a)>(b) ? (a) : (b))
#define focus_cursor_p(s) \
	(((s) == selected_screen) && ((s)->display.x->focus_p))

#define NO_CURSOR	0
#define NORMAL_CURSOR	1
#define THIN_CURSOR	2
#define NO_THIN_CURSOR	3
#define SOLID_CURSOR(cursor) \
	(((cursor) != NO_CURSOR) && ((cursor) != NO_THIN_CURSOR))

/* Nonzero after BLOCK_INPUT; prevents input events from being
   processed until later.  */
int interrupt_input_blocked;

/*
#if defined (SIGIO) && defined (FIONREAD)
int BLOCK_INPUT_mask;
#endif
*/


/* Stuff for dealing with the title. */
static char *hostname;
static char *id_name;

/* This is the X connection that we are using.  */
Display *x_current_display;


/* File descriptor of the X connection.  Used in sysdep.c to set the
   X connection in SIGIO mode.  */
int x_file_descriptor;

/* Screen being updated by update_screen.
   
   This is set by XTupdate_begin and looked at by all the
   XT functions.  It is zero while not inside an update.
   In that case, the XT functions assume that `selected_screen'
   is the screen to apply to.  */
extern struct screen *updating_screen;

/* During an update, maximum vpos which ins/del of lines
   may affect.  This is  specified by higher levels.

   This is the number of lines, from the top of screen downwards,
   which can participate in insert-line/delete-line operations.

   Effectively it excludes the bottom screen_height - specified_window_size
   lines from those operations.  */
static int update_height;

/* Number of pixels below each line. */
int x_interline_space;

/* Nonzero enables some debugging for the X interface code. */
extern int _Xdebug;

/* Remember if the last cursor displayed was a bar or a box */
/* static Lisp_Object Vlast_bar_cursor; */

extern find_window (struct window *w, Lisp_Object win);

static void update_cursor_position (struct line_header *l,
				    struct char_block *cb,
#ifdef I18N4
				    wchar_t *text,
#else
				    unsigned char *text,
#endif
				    int cursor, struct face *face,
				    struct window_mirror *mirror);
static int CursorToggle (struct screen *s);
static void ClearCursor (struct screen *s);
static void dump_windows (Lisp_Object window, 
                          int top, int left, int rows, int cols);
static void dump_window (Lisp_Object window,
                         int top, register left, int rows, int cols);

static void PlotTextLine (struct window *w, struct line_header *l,
                          struct char_block *start, struct char_block *end,
                          char clear, int line_type);
#ifdef I18N4
static void ShipOutTextBlock (wchar_t *str, int count, 
                              int x, int y, int a, int d,
                              int cursor, struct face *face, 
                              struct window_mirror *mir);
#else
static void ShipOutTextBlock (unsigned char *str, int count, 
                              int x, int y, int a, int d,
                              int cursor, struct face *face, 
                              struct window_mirror *mir);
#endif
static void ShipOutGlyphBlock (GLYPH index, int x, int y, int a, int d,
                               unsigned short width,
			       int cursor, struct face *face, 
                               struct window_mirror *mir);
static Bool ShipOutBlankBlock (Bool margin, int width, int x, int y, int a,
			       int d, int cursor, struct face *face, 
                               struct window_mirror *mir);
static void XTcolor_area (struct screen *s, unsigned long color, int x, int y,
			  int width, int height);
static void XTclear_window_end (struct window *w, int ypos1, int ypos2);
static void XTshift_region (struct window *w, struct line_header *start,
                            struct line_header *end);
static void XTcursor_to (struct line_header *l, struct char_block *cb, 
                         int row, int col, 
                         struct window_mirror *mir, struct screen *s);
static void InsertChar (struct window *w, struct line_header *l, 
                        struct char_block *new,
                        struct char_block *cb, struct char_block *end, 
                        char clear);
/* Get it past C type-check */
#ifdef I18N4
static int XTtext_width (Lisp_Object, CONST wchar_t *,
			 int);
#else
static int XTtext_width (Lisp_Object, CONST unsigned char *,
			 int);
#endif

/************************************************************************/
/*			terminal hook routines				*/
/************************************************************************/

/* These hooks are called by update_screen at the beginning and end
   of a screen update.  We record in `updating_screen' the identity
   of the screen being updated, so that the XT... functions do not
   need to take a screen as argument.  Most of the XT... functions
   should never be called except during an update, the only exceptions
   being XTcursor_to, and XTwrite_glyphs.  */

extern struct window *find_window_by_pixel_pos (unsigned int pix_x,
						unsigned int pix_y,
						Lisp_Object win);
void
CXTupdate_begin (w)
     struct window *w;
{	
  struct screen *s = XSCREEN(w->screen);
  struct window *cur_w;
  Lisp_Object win;

  /* If the window configuration is changing, we don't want to erase
     the cursor unless it is still in the same window.  Otherwise we
     might accidentally draw over part of another window. */
  XSETR (win, Lisp_Window, w);
  if (s->cur_line && s->cur_char)
    {
      cur_w = find_window_by_pixel_pos (s->cur_char->xpos, s->cur_line->ypos,
					win);
    }

  updating_screen = s;
  update_height = SCREEN_HEIGHT(updating_screen);

  BLOCK_INPUT;
  if (!s->cursor_erased && w == XWINDOW(s->selected_window))
    {
      if (w == cur_w)
	CursorToggle (s);
      else
	s->cursor_erased = 1;
    }
  UNBLOCK_INPUT;

/*
  if (!EQ (Vlast_bar_cursor, Vbar_cursor))
    s->cursor_erased = x_display_cursor (s, 0);
*/
}

static void
XTupdate_begin (struct screen *s) /* >>> arg ignored */
{
  CXTupdate_begin (XWINDOW (selected_window));
}

enum window_type
{
  scrollbar_window,
  text_window
};

/* Nonzero when emacs is garbage-collecting. */
extern int gc_in_progress;

void
CXTupdate_end (w)
     struct window *w;
{
  SCREEN_PTR s = XSCREEN(w->screen);

  BLOCK_INPUT;

  if (!s->cursor_erased && s == selected_screen &&
      w == XWINDOW(s->selected_window))
    {
      CursorToggle (s);
    }
  updating_screen = 0;

  XFlushQueue ();
  UNBLOCK_INPUT;
}

static void
XTupdate_end (struct screen *s) /* >>> ignores arg */
{
  CXTupdate_end (XWINDOW(selected_window));
}



/*
 * Fastmove_cursor - called from output_for_insert().  Note:  in this case,
 * a character has just been inserted (overwriting past cursor), and cursor
 * needs to be redrawn in its new spot.
 */
void
Fastmove_cursor(struct screen *s)
{
  /* Fudge so we are toggling the cursor BACK into existence. */

  s->cursor_erased = 1;
  s->phys_cursor_x = s->cursor_x;
  BLOCK_INPUT;
  CursorToggle(s);
  XFlushQueue ();
  UNBLOCK_INPUT;
}



/*
 * XTcursor_to moves the cursor to the correct location and checks whether an
 * update is in progress in order to toggle it on.
 */
static void
XTcursor_to (struct line_header *l, struct char_block *cb, int row,
	     int col, struct window_mirror *mir, struct screen *s)
{
  /* #### kludge; this can be called from reset_sys_modes after the X
     display has gone away because of KillClient... */
  if (x_current_display == 0) return;

  BLOCK_INPUT;

  if (updating_screen)
    {
      /* Cursor is already dead.  Now put it in its place. */
      s->cur_mir = mir;
      s->cur_line = l;
      s->cur_char = cb;
      s->phys_cursor_x = col;
      s->phys_cursor_y = row;       
      if (!s->cursor_erased)
	CursorToggle (s);
      UNBLOCK_INPUT;
      return;
      /* Generally, XTmove_cursor will be invoked
       * when InUpdate with !CursorExists 
       * so that wasteful XFlush is not called
       */
    }
  if (s->cur_mir &&
      (s->new_cur_mir != s->cur_mir &&
       s->cur_mir != find_window_mirror (XWINDOW(s->minibuffer_window))) &&
      !NILP (real_window (s->cur_mir, 1)) &&
      find_window(XWINDOW (real_window (s->cur_mir, 0)), s->root_window) &&
      s->cur_char && s->cur_line)
    {
#ifdef I18N4
      wchar_t a[2];
#else
      unsigned char a[2];
#endif
      struct face *face = s->cur_char->char_b ? &SCREEN_NORMAL_FACE(s)
	: &SCREEN_LEFT_MARGIN_FACE(s);
      a[0] = s->cur_char->ch == 0 ? ' ' : s->cur_char->ch;
      a[1] = 0;
	  
      /* UGLY:  Cursor's previous position may be in another window
       * still being displayed; this will result in a blank or stray
       * cursor left in that other window.  Find the window, and
       * erase the old cursor
       */

      update_cursor_position (s->cur_line, s->cur_char, a, NO_CURSOR,
			      s->cur_char->face ? s->cur_char->face : face,
			      s->cur_mir);

      s->cursor_erased = 1;
      s->cur_mir = mir;
      s->cur_line = l;
      s->cur_char = cb;
      s->phys_cursor_x = col;
      s->phys_cursor_y = row;
      CursorToggle(s);
      UNBLOCK_INPUT;
      return;
    }
  if ((row == s->phys_cursor_y) && (col == s->phys_cursor_x) &&
      ((s->cur_mir && !NILP (real_window (s->cur_mir, 1)) &&
	!MINI_WINDOW_P (XWINDOW (real_window (s->cur_mir, 0))))
       || (mir && MINI_WINDOW_P (XWINDOW (real_window (mir, 0))))))
    {
      /* If these are not actually the same then we may actually not
         have a cursor visible, so force one into existence. */
      if (s->cur_char != cb)
	s->cursor_erased = 1;

      s->cur_mir = mir;
      s->cur_line = l;
      s->cur_char = cb;
      if (s->cursor_erased)
	CursorToggle (s);
      XFlushQueue ();
      UNBLOCK_INPUT;
      return;
    }
  /*
   * First remove cursor from where it is
   */
  if (!s->cursor_erased)
    CursorToggle (s);
  /*
   * Now update ptrs to where cursor actually is
   */
  s->cur_mir = mir;
  s->cur_line = l;
  s->cur_char = cb;  
  s->phys_cursor_x = col;
  s->phys_cursor_y = row;
  /*
   * Now plot it
   */
  if (s->cursor_erased)
    CursorToggle (s);
  XFlushQueue();
  UNBLOCK_INPUT;

}



/*
 * Used for Expose events.  Have to get the text
 * back into the newly blank areas.
 */
void
Cdumprectangle (register int top, register int left, register int rows,
		register int cols, struct screen *s)
{
  if (s == selected_screen) ClearCursor(s);
  
  dump_windows (s->root_window,top,left,rows,cols);

  if (!updating_screen && s == selected_screen && s->cursor_erased)
    CursorToggle(s);

  return;
}



static void
dump_windows (Lisp_Object window, register int top, register int left,
	      register int rows, register int cols)
{
  for (; !NILP(window); window = XWINDOW(window)->next)
    dump_window (window,top,left,rows,cols);
}


static void
dump_window (Lisp_Object window, register int top, register int left,
	     register int rows, register int cols)
{
  struct window *w = XWINDOW(window);
  struct screen *s = XSCREEN(w->screen);
  struct line_header *l;
  struct char_block *cb,*end;
  int pixright = w->pixleft + w->pixwidth;
  int pixbot = w->pixtop + w->pixheight;
  int startx,starty,endx,endy,margin_x;

  if (!NILP(w->vchild))
    {
      dump_windows (w->vchild,top,left,rows,cols);
      return;
    }
  if (!NILP(w->hchild))
    {
      dump_windows (w->hchild,top,left,rows,cols);
      return;
    }
  if (NILP(w->buffer))
    abort();			/* No buffer...we're screwed */

  if (SCREENP(Vglobal_minibuffer_screen) && EQ(window,s->minibuffer_window)
      && s != XSCREEN(Vglobal_minibuffer_screen))
    return;

  /*
   * Find out if expose region intersects this window; if not, return.
   */
  if (left > pixright || (left + cols) < w->pixleft
      || top > pixbot || (top + rows) < w->pixtop)
    return;

  /*
   * Calc top and left of expose region for this window.
   */
  startx = max (w->pixleft, left);
  endx = min (pixright, left + cols);
  starty = max (w->pixtop, top);
  endy = min (pixbot, top + rows);
  margin_x = w->pixleft + LEFT_MARGIN (XBUFFER (w->buffer), s, w);

  /*
   * Clear the margin area to reset its background.
   */
  if (startx < margin_x)
    {
      XTcolor_area (s, FACE_BG_PIXEL (&SCREEN_LEFT_MARGIN_FACE(s)),
		    startx, starty,
		    (min (margin_x, endx) - startx), (endy - starty));
      if (s->cur_char->xpos == SCREEN_INT_BORDER (s) && SCREEN_INT_BORDER (s))
	XClearArea (x_current_display, XtWindow (s->display.x->edit_widget),
		    0, starty,
		    SCREEN_INT_BORDER (s), (endy - starty), 0);
    }

  /*
   * Find starting line corresponding to this start ypos
   */
  l = window_display_lines (w);
  while (l && (int) (l->ypos + l->descent) < starty)
    l = l->next;

  while (l && (int) (l->ypos - l->ascent) <= endy)
    {
      if (l->in_display == -1)
	{
	  /* Line is being layed-out in redisplay process, so give it
	   * the left edge of expose region
	   */
	  l->in_display = startx;
	}
      else if (l->ypos)		/* Does line have vertical position? */
	{
	  cb = l->body;
	  while (cb && cb != l->end && (cb->xpos + cb->width) < startx)
	    cb = cb->next;
	  if (cb != l->end)
	    {
	      end = cb;
	      while (end && end != l->end && (end->xpos) <= endx)
		end = end->next;

	      /*
	       * Note we will not clear to the end of line here.  If
	       * expose event doesn't cover end of line, no need to plot
	       * (clear) it; if it does, it is clear anyway.
	       */
	      PlotTextLine (w,l,cb,end,0,BODY); /* Replot the line */
	    }
	  cb = l->margin_start;
	  while (cb && cb != l->margin_end && (cb->xpos + cb->width) < startx)
	    cb = cb->next;
	  if (cb != l->margin_end)
	    {
	      end = cb;
	      while (end && end != l->margin_end && (end->xpos <= endx))
		end = end->next;

	      PlotTextLine (w,l,cb,end,0,MARGIN); /* Replot the margin */
	    }
	}
      l = l->next;
    }
  /*
   * Check modeline for window
   */
  if (!EQ(window,s->minibuffer_window) && window_display_modeline (w))
    {
      l = window_display_modeline (w);

      if (l)
	{
	  cb = l->body;
	  while (cb && cb != l->end && (cb->xpos + cb->width) < startx)
	    cb = cb->next;
	  if (cb != l->end)
	    {
	      end = cb;
	      while (end && end != l->end && (end->xpos) <= endx)
		end = end->next;
	      PlotTextLine (w,l,cb,end,0,BODY); /* Replot the mode line */
	    }
	}
    }
  return;
}

static void
update_cursor_position (struct line_header *l, struct char_block *cb,
#ifdef I18N4
			wchar_t *text,
#else
			unsigned char *text,
#endif
			int cursor, struct face *face,
			struct window_mirror *mirror)
{
  if (cb->char_b &&
      ((l->margin_start == l->margin_end)
       || (cb->xpos >= l->margin_end->xpos)))
    {
      if (NILP (real_window (mirror, 1)))
	/* If real_window returns nil then we are going to crash and
	   burn immediately upon entry into ShipOutTextBlock.  So
	   don't go there.  This crash seems to be appearing
	   exclusively when coming from the cursor code now so I'm
	   guessing some early patches took care of those problems.
	   The cursor code continues to be a mess with some real
	   timing problems, though.  This bug would normally receive a
	   lot more attention but 1) it occurs very infrequently
	   making it difficult to work on and 2) all of this code is
	   getting purged within a month or so.  So I'm settling for
	   getting rid of the crash. */
	return;
      ShipOutTextBlock (text, 1, cb->xpos, l->ypos, l->ascent, l->descent,
			(cursor == NO_CURSOR
			 ? (cb->next == NULL
			    ? NO_THIN_CURSOR
			    : cursor)
			 : cursor), face, mirror);
     }
  else
    {
      struct char_block *glyph;

      if (cb->char_b)
	glyph = l->margin_start;
      else
	glyph = cb;

      /* skip any margin glyphs before the character where the
	 cursor used to be */
      if (cb->char_b)
	while (glyph && ((glyph->xpos + glyph->width) < cb->xpos))
	  glyph = glyph->next;

      /* If the cursor is over the margin area then the very first
         check will cause it to fail to get erased. */
      if (cb->char_b && text[0] == ' ')
	ShipOutTextBlock (text, 1, cb->xpos, l->ypos, l->ascent, l->descent,
			  cursor, face, mirror);

      /* redo all glyphs which overlap the character area */
      if (!(cb->char_b && text[0] == ' ' && cursor))
	do
	  {
	    if (glyph)
	      {
		Lisp_Object p = glyph_to_pixmap (glyph->glyph);

		if (glyph->blank)
		  {
		    ShipOutBlankBlock (False, glyph->width, glyph->xpos,
				       l->ypos, l->ascent, l->descent,
				       cursor, face, mirror);
		  }
		else if (PIXMAPP (p) || SUBWINDOWP (p))
		  {
		    /* The width stuff is a hack because something is
		       resetting glyph->width when it shouldn't be.  This
		       code is going to get purged soon so I'm not going
		       to spend any more time looking for the real problem
		       at the moment. */
		    unsigned short width;
	    
		    if (glyph->width)
		      width = glyph->width;
		    else
		      width = glyph_width (glyph->glyph, FACE_FONT (face));

		    ShipOutGlyphBlock (glyph->glyph, glyph->xpos,
				       l->ypos, l->ascent, l->descent,
				       width, cursor, face, mirror);
		  }
		else if (STRINGP (p))
		  {
#ifdef I18N4
		    safe_mbstowcs ((char *) XSTRING (p)->data, &wc_buf);
#endif
		    ShipOutTextBlock (
#ifdef I18N4
				      wc_buf.data, (wc_buf.in_use + 1),
#else
				      XSTRING (p)->data, XSTRING (p)->size,
#endif
				      glyph->xpos,
				      l->ypos, l->ascent, l->descent,
				      cursor, face, mirror);
		  }
		else if (NILP (p))
		  {
		    /* glyphs are now initialized in redisplay to -1, a
		       bogus value, since 0 is actually a valid glyph.
		       Various timing "problems" can lead to the cursor
		       getting to this point on an uninitialized redisplay
		       block.  That is potentially a sign of a bigger
		       problem, but I'm not tracking it down in this
		       redisplay code.  With bogus values, glyph_to_pixmap
		       will return nil so in that case we simply do
		       nothing. */
		  }
		else
		  {
		    abort();	/* we don't know what the hell we've got */
		  }

		if (cb->char_b)
		  glyph = glyph->next;
	      }
	  } while (cb->char_b && glyph && (glyph->xpos <(cb->xpos+cb->width)));
    }

#ifndef I18N4
  /*
   * If we are erasing the cursor then starting with the character after
   * the old cursor location start redrawing characters as long as they
   * have an lbearing value.
   */
  /* Turned off because cb isn't actually correct most of the time and
     thus this ends up introducing a bug.  Something like this needs
     to be added, but I'm not going to worry about it anymore at the
     moment.  The new redisplay will take care of it and it is a low
     priority thing in any case. */
#if 0
  if (!cursor)
    {
      XFontStruct *font = XFONT (face->font)->font;
      XCharStruct overall_return;
      struct char_block *cur_cb = cb->next;
      int has_lbearing = 1;
      unsigned char a[2];
      int direction_return, font_ascent_return, font_descent_return;

      a[1] = 0;
      while (cur_cb && cur_cb->char_b && has_lbearing)
	{
	  a[0] = cur_cb->ch == 0 ? ' ' : cur_cb->ch;
	  XTextExtents (font, (char *) a, 1,
			&direction_return, &font_ascent_return,
			&font_descent_return, &overall_return);

	  has_lbearing = overall_return.lbearing ? 1 : 0;

	  if (has_lbearing)
	    ShipOutTextBlock (a, 1, cur_cb->xpos, l->ypos, l->ascent,
			      l->descent, cursor, cur_cb->face, mirror);
	  cur_cb = cur_cb->next;
	}
    }
#endif
#endif /* !I18N4 */
}


/*
 * Artificially creating a cursor is hard, the actual position on the
 * screen (either where it is or last was) is tracked with vix_x,y.
 * Gnu Emacs code tends to assume a cursor exists in hardward at ws->cursor_x,y
 * and that output text will appear there.  During updates, the cursor is
 * supposed to be blinked out and will only reappear after the update
 * finishes.
 */
static int
CursorToggle (struct screen *s)
{
#ifdef I18N4
  wchar_t a[2];
#else
  unsigned char a[2];
#endif
  int wid;
  struct window *w = XWINDOW(s->selected_window);
  Widget widget = s->display.x->edit_widget;
  Window x_win = XtWindow (widget);
  struct Lisp_Font *font = XFONT (SCREEN_DEFAULT_FONT (s));
  struct face *face = &SCREEN_NORMAL_FACE (s);

  if (!s->visible)
    {
      s->cursor_erased = 1;
      return 0;
      /* Currently the return values are not
       * used, but I could anticipate using
       * them in the future.
       */
    }

  if (s->phys_cursor_y < 0)
    {
      /* Not much can be done */
      XFlushQueue ();
      s->cursor_erased = 1;
      return 0;
    }

  /*
   * This may get called before any layout is done.  Blow if off in that
   * case.
   */

  if (!s->cur_line || !s->cur_char || !s->cur_mir
      || s->cur_char->xpos < w->pixleft
      || s->cur_char->xpos > w->pixleft + w->pixwidth)
    {
      XFlushQueue ();
      s->cursor_erased = 1;
      return 0;
    }

  a[1] = 0;
  /* Epoch:
   * Potential problem:  cursors in minibuffer screen and current edit
   * screen.  If we are toggling cursor on screen other than current screen,
   * force it to not exist.
   */
#if 0
  if (s != selected_screen) s->cursor_erased = 0;
  /* Die a horrible death, cursor */
#endif

  if (s->phys_cursor_x <= s->cur_line->chars)
    {
      a[0] = s->cur_char->ch == 0 ? ' ' : s->cur_char->ch;
      if (s->cur_char->face)
	face = s->cur_char->face;
      else if (s->cur_char->ch == 0 && s->cur_char->prev != 0)
	if (s->cur_char->prev->face)
	  face = s->cur_char->prev->face;
      if (face == &SCREEN_MODELINE_FACE(s))
	face = &SCREEN_NORMAL_FACE(s);
      font = XFONT (FACE_FONT (face));
      if (s->cur_char->char_b)
#ifdef I18N4
	wid = XwcTextEscapement (font->font, a, 1);
#else
	wid = XTextWidth (font->font, (char *) a,1);
#endif
      else
	wid = glyph_width (s->cur_char->glyph, FACE_FONT (face));

      if (!s->cursor_erased)
	{
	  update_cursor_position (s->cur_line, s->cur_char, a, NO_CURSOR,
				  face, s->cur_mir);
	}
      else
	{
	  update_cursor_position (s->cur_line, s->cur_char, a,
				  (s->cur_char->next == NULL ?
				   THIN_CURSOR : NORMAL_CURSOR),
				  face, s->cur_mir);
	}
    }
  else
    {
      a[0] = s->cur_char->ch == 0 ? ' ' : s->cur_char->ch;
      if (s->cur_char->face)
	face = s->cur_char->face;
      else if (s->cur_char->ch == 0 && s->cur_char->prev != 0)
	if (s->cur_char->prev->face)
	  face = s->cur_char->prev->face;
      if (face == &SCREEN_MODELINE_FACE(s))
	face = &SCREEN_NORMAL_FACE(s);
      font = XFONT (FACE_FONT (face));
      if (s->cur_char->char_b)
#ifdef I18N4
	wid = XwcTextEscapement (font->font, a, 1);
#else
	wid = XTextWidth (font->font, (char *) a, 1);
#endif
      else
	wid = glyph_width (s->cur_char->glyph, FACE_FONT (face));
      if (!s->cursor_erased)
	{
	  int height = s->cur_line->ascent ?
	    (s->cur_line->ascent + s->cur_line->descent) : font->height;

          if (!SCREENP(Vglobal_minibuffer_screen)
	      && !NILP (real_window (s->cur_mir, 1))
	      && EQ (real_window (s->cur_mir, 0), s->minibuffer_window))
	    {
	      ;
	    }
	  else
	    {
	      XClearArea(x_current_display, x_win,
			 s->cur_char->xpos,
			 s->cur_line->ypos - s->cur_line->ascent,
			 wid, height, 0);

	      if (s->cur_char->xpos == SCREEN_INT_BORDER (s)
		  && SCREEN_INT_BORDER (s))
		XClearArea (x_current_display, x_win, 0,
			    s->cur_line->ypos - s->cur_line->ascent,
			    SCREEN_INT_BORDER (s), height, 0);
	    }
	}
      else
	{
	  /* I must be blind because I don't see how s->cur_mir can
	     be nil when this point is reached.  But it is apparently
	     happening. */
	  if (!s->cur_mir)
	    {
	      XFlushQueue ();
	      s->cursor_erased = 1;
	      return 0;
	    }
	  else
	    {
	      update_cursor_position (s->cur_line, s->cur_char, a,
				      (s->cur_char->next == NULL ?
				       THIN_CURSOR : NORMAL_CURSOR),
				      face, s->cur_mir);
	    }
	}
    }

  s->cursor_erased = !s->cursor_erased;

  if (!updating_screen)
    XFlushQueue();

  return 1;
}



/*
 * This routine is used by routines which are called to paint regions 
 * designated by Expose events.  If the cursor may be in the exposed
 * region, this routine makes sure it is gone so that dumprectangle can 
 * toggle it back into existance if dumprectangle is invoked when not in
 * the midst of a screen update.
 */
static void
ClearCursor(struct screen *s)
{
#ifdef I18N4
  wchar_t a[1];
#else
  unsigned char a[1];
#endif
  int wid,height;
  struct Lisp_Font *font = XFONT (SCREEN_DEFAULT_FONT (s));
  
  BLOCK_INPUT;
  if (!s->visible)
    {
      s->cursor_erased = 1;
      UNBLOCK_INPUT;
      return;
    }
	
  if (s->phys_cursor_x >= s->width || s->phys_cursor_y < 0 ||
      s->phys_cursor_y >= s->height || !s->new_cur_char || !s->new_cur_line)
    {
      /* Not much can be done */
      s->cursor_erased= 1;
      UNBLOCK_INPUT;
      return;
    }

  a[0] = ' ';
  if (s->new_cur_char->face)
    font = XFONT (FACE_FONT (s->new_cur_char->face));
#ifdef I18N4
  wid = s->new_cur_char->ch == 0 ? XwcTextEscapement (font->font, a, 1) : 
    s->new_cur_char->width;
#else
  wid = s->new_cur_char->ch == 0 ? XTextWidth (font->font, (char *) a, 1) : 
    s->new_cur_char->width;
#endif
  height = s->new_cur_line->ascent ?
    (s->new_cur_line->ascent + s->new_cur_line->descent) : font->height;

  XClearArea (x_current_display, XtWindow (s->display.x->edit_widget),
	      s->new_cur_char->xpos,
	      s->new_cur_line->ypos - s->new_cur_line->ascent,wid,
	      height,0);
  if (s->cur_char->xpos == SCREEN_INT_BORDER (s) && SCREEN_INT_BORDER (s))
    XClearArea (x_current_display, XtWindow (s->display.x->edit_widget), 0,
		s->new_cur_line->ypos - s->new_cur_line->ascent,
		SCREEN_INT_BORDER (s), height, 0);

  s->cursor_erased = 1;
  UNBLOCK_INPUT;
}



/*
 * Plot a line L (or portion of line from START to END) of text in window W.
 * START and END are considered, if non-zero
 */
static void
PlotTextLine (struct window *w, struct line_header *l,
	      struct char_block *start, struct char_block *end, char clear,
	      int line_type)
{
  struct screen *s = XSCREEN(w->screen);
#ifdef I18N4
  wchar_t buf[1000];	/* Buffer for constructing string. */
  wchar_t *pos;		/* Position in buf */
#else
  unsigned char buf[1000];	/* Buffer for constructing string. */
  unsigned char *pos;		/* Position in buf */
#endif
  struct char_block *cb;	/* Current char in line */
  struct face *f;		/* Current style for plotting */
  int n = 0;			/* char count for current region */
  int xpos;			/* left pixel position of a block */
  struct char_block *start_block,*end_block;

  if (line_type == BODY)
    {
      start_block = l->body;
      end_block = l->end;
    }
  else
    {
      start_block = l->margin_start;
      end_block = l->margin_end;
    }

  if (l == 0 || l->ypos == 0) abort();
  pos = buf;
  cb = start ? start : start_block;
  while (cb != start_block && cb->prev->ch == ' ')
    cb = cb->prev;
  xpos = cb->xpos;
  f = cb->face;

  BLOCK_INPUT;
  while (cb && cb != end_block)
    {
      if (cb->face == f && n < 999 && cb->char_b && !cb->blank)
	{
	  *pos++ = cb->ch;
	  /* Update attributes */
	  cb->changed = cb->new = 0;
	  n++;
	  if (cb == end) break;	  
	  cb = cb->next;
	}
      else
	{
	  /* Time to ship out a block */
	  ShipOutTextBlock(buf,n,xpos,l->ypos,l->ascent,l->descent,
			   NO_CURSOR, f, find_window_mirror (w));
	  if (cb->blank)
	    {
	      cb->changed = cb->new = 0;
	      ShipOutBlankBlock (True, cb->width, cb->xpos, l->ypos,
				 l->ascent, l->descent, NO_CURSOR,
				 cb->face, find_window_mirror (w));
	      cb = cb->next;
	    }
	  else if (!cb->char_b)
	    {
	      Lisp_Object p = glyph_to_pixmap (cb->glyph);

	      cb->changed = cb->new = 0;
	      if (PIXMAPP (p) || SUBWINDOWP (p))
		ShipOutGlyphBlock (cb->glyph,cb->xpos,l->ypos,l->ascent,
				   l->descent, cb->width, NO_CURSOR, cb->face,
				   find_window_mirror (w));
	      else if (STRINGP (p))
		{
#ifdef I18N4
		  safe_mbstowcs ((char *) XSTRING (p)->data, &wc_buf);
#endif
		  ShipOutTextBlock (
#ifdef I18N4
				    wc_buf.data, (wc_buf.in_use + 1),
#else
				    XSTRING (p)->data, XSTRING (p)->size,
#endif
				    cb->xpos,l->ypos,l->ascent,
				    l->descent, NO_CURSOR, cb->face,
				    find_window_mirror (w));
		}
	      else
		abort ();

	      cb = cb->next;
	    }
	  if (cb)
	    {
	      f = cb->face;
	      xpos = cb->xpos;
	    }
	  n = 0;
	  pos = buf;
	}
    }
  /* Ship out dangling stuff (can only have dangling text) */
  if (n)
    ShipOutTextBlock(buf,n,xpos,l->ypos,l->ascent,l->descent,
		     NO_CURSOR, f, find_window_mirror (w));
  if (line_type == BODY && clear && ((int) (l->ascent + l->descent) > 0))
    if (l->lwidth < (w->pixleft + w->pixwidth - 1))
      {
	XClearArea(x_current_display, XtWindow (s->display.x->edit_widget),
		   end_block->xpos,(l->ypos - l->ascent),
		   (w->pixleft + w->pixwidth - l->lwidth),
		   (l->ascent + l->descent),0);
	if (xpos == SCREEN_INT_BORDER (s)
	    && !LEFT_MARGIN (XBUFFER (w->buffer), s, w)
	    && SCREEN_INT_BORDER (s))
	  XClearArea (x_current_display, XtWindow (s->display.x->edit_widget),
		      0, (l->ypos - l->ascent),
		      SCREEN_INT_BORDER (s), (l->ascent + l->descent), 0);
      }
  if (!l->modeline
      && l->mwidth < (w->pixleft + LEFT_MARGIN (XBUFFER (w->buffer), s, w)))
    {
      int width = (w->pixleft + LEFT_MARGIN (XBUFFER (w->buffer), s, w)
		   - l->mwidth);
      
      if (width)
	XTcolor_area (s, FACE_BG_PIXEL (&SCREEN_LEFT_MARGIN_FACE(s)),
		      l->mwidth,(l->ypos - l->ascent),
		      width, (l->ascent + l->descent));
    }

  UNBLOCK_INPUT;
}

/* Returns whether it actually cleared anything.
   (It doesn't need to if the font/glyph to be drawn exactly fills the
   target area.)
 */
static Bool
ShipOutBlankBlock (Bool margin, int width, int x, int y, int a, int d,
		   int cursor, struct face *face, struct window_mirror *mir)
{
  Display *dpy = x_current_display;
  struct window *w = XWINDOW (real_window (mir, 0));
  struct screen *s = XSCREEN (w->screen);
  Window x_win = XtWindow (s->display.x->edit_widget);
  GC gc;
  XGCValues gcv;
  unsigned long bg_mask;
  struct face *f;
  struct Lisp_Font *font;
  Bool clear_rect_p;

  memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
  gcv.graphics_exposures = False;

  f = face ? face :
    (margin ? &SCREEN_LEFT_MARGIN_FACE(s) : &SCREEN_NORMAL_FACE(s));
  font = XFONT (FACE_FONT (f));

  /*
   * There are three possible ways to blank an area.
   *  - drawing a rectangle in the background color
   *    (which we do implicitly, by using XDrawImageString instead
   *     of XDrawString)
   *  - blitting in a bitmap, whose fg and bg match that of the text
   *  - blitting in a pixmap, which carries its colors with it
   * The gcv that is used for these operations doesn't have its font
   * specified, to maximize its reusability.
   */
  
  bg_mask = GCGraphicsExposures;
  if (NILP (f->back_pixmap))
    {
      clear_rect_p = False;
    }
  else if (XPIXMAP (f->back_pixmap)->depth == 0)
    {
      if (SOLID_CURSOR (cursor) && focus_cursor_p (s))
	{
	  gcv.foreground = FACE_BG_PIXEL (f);
	  gcv.background = FACE_FG_PIXEL (f);
	}
      else
	{
	  gcv.foreground = FACE_FG_PIXEL (f);
	  gcv.background = FACE_BG_PIXEL (f);
	}
      gcv.fill_style = FillOpaqueStippled;
      gcv.stipple    = XPIXMAP (f->back_pixmap)->pixmap;
      bg_mask |= (GCForeground | GCBackground | GCStipple | GCFillStyle);
      clear_rect_p = True;
    }
  else if (!SOLID_CURSOR (cursor) || !focus_cursor_p (s))
    {
      /* If the cursor is over tiled area, don't draw the tile, because
	 then the cursor wouldn't be visible (we can't invert colors when
	 there is a tile, because it carries its colors inside it.)
	 */
      gcv.fill_style = FillTiled;
      gcv.tile = XPIXMAP (f->back_pixmap)->pixmap;
      bg_mask |= (GCTile | GCFillStyle);
      clear_rect_p = True;
    }
  else
    {
      clear_rect_p = False;
    }

  if (!clear_rect_p && (margin
			|| (cursor == THIN_CURSOR)
			|| (cursor == NO_THIN_CURSOR)
			|| (!SOLID_CURSOR (cursor)
			    && ((int) FONT_ASCENT(font->font) < a
				|| (int) FONT_DESCENT(font->font) < d))))
    {
    /* If the height of the selected font is less than the line being
       redisplayed, then calling XDrawImageString won't clear the area
       completely.
       */
      gcv.foreground = FACE_BG_PIXEL (f);
      gcv.fill_style = FillSolid;
      bg_mask |= (GCForeground | GCFillStyle);
      clear_rect_p = True;
    }

  if (clear_rect_p)
    {
      /* Get a GC and draw the rectangle.
       */
      gc = gc_cache_lookup (the_gc_cache, &gcv, bg_mask);
      /* Make sure we erase all of that pesky end-of-line cursor */
      if ((cursor == THIN_CURSOR || cursor == NO_THIN_CURSOR)
	  && width < EOL_CURSOR_WIDTH)
	width = EOL_CURSOR_WIDTH;
      XFillRectangle (dpy, x_win, gc, x, y - a, width, a + d);
    }

  return clear_rect_p;
}


static void
ShipOutGlyphBlock (GLYPH index, int x, int y, int a, int d,
		   unsigned short width, int cursor, struct face *face,
		   struct window_mirror *mir)
{
  struct window *w = XWINDOW (real_window (mir, 0));
  struct screen *s = XSCREEN (w->screen);
  Display *dpy = x_current_display;
  Window x_win = XtWindow (s->display.x->edit_widget);
  GC gc;
  XGCValues gcv;
  unsigned long glyph_mask;
  struct face *f;
  struct Lisp_Font *font;
  Lisp_Object p = glyph_to_pixmap (index);
  int bitmap_y_offset = 0;
  int height;
  Pixel cursor_color;

  if (NILP (p))
    abort ();

  /* We now pass in the width because redisplay may have decided to
     clip the pixmap. */
  /*  width = XPIXMAP (p)->width; */
  if (PIXMAPP (p))
    height = XPIXMAP (p)->height;
  else if (SUBWINDOWP (p))
    height = XSUBWINDOW (p)->height;
  else
    abort();

  memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
  gcv.graphics_exposures = False;

  f = face ? face : &SCREEN_LEFT_MARGIN_FACE(s);
  font = XFONT (FACE_FONT (f));

  BLOCK_INPUT;
  XtVaGetValues (s->display.x->edit_widget, XtNcursorColor, &cursor_color, 0);
  UNBLOCK_INPUT;

  /*
   * First we need to erase the area where the glyph is going to be
   * drawn.
   */
  {
    unsigned short blank_width;

    blank_width = w->pixleft + w->pixwidth - x;
    if (width < blank_width)
      blank_width = width;

    ShipOutBlankBlock (True, blank_width, x, y, a, d, cursor, face, mir);
  }

  glyph_mask = GCForeground | GCBackground | GCGraphicsExposures;
  gcv.foreground = FACE_FG_PIXEL (f);
  gcv.background = FACE_BG_PIXEL (f);

  bitmap_y_offset = (height - (a+d)) / 2;
  if (height > (a+d))
    height = a+d;

  /*
   * See comments about cursors in ShipOutTextBlock.
   */
  if (focus_cursor_p (s) && NILP (Vbar_cursor) && SOLID_CURSOR(cursor))
    {
      XTcolor_area (s, cursor_color, x, y-a, width, a+d);
    }

  if (PIXMAPP (p))
    {
      if (XPIXMAP (p)->mask)
	{
	  gcv.function = GXcopy;
	  gcv.clip_mask = XPIXMAP (p)->mask;
	  gcv.clip_x_origin = x;
	  gcv.clip_y_origin = y - a - bitmap_y_offset;
	  glyph_mask |= GCFunction | GCClipMask | GCClipXOrigin
	    | GCClipYOrigin;
	}

      gc = gc_cache_lookup (the_gc_cache, &gcv, glyph_mask);

      /* depth of 0 means it's a bitmap, not a pixmap, and we should
	 use XCopyPlane (1 = current foreground color, 0 = background)
	 instead of XCopyArea, which means that the bits in the pixmap
	 are actual pixel values, instead of symbolic of fg/bg.
	 */
      if (XPIXMAP (p)->depth > 0)
	XCopyArea (dpy, XPIXMAP (p)->pixmap, x_win, gc, 0, bitmap_y_offset,
		   width, height, x, y-a);
      else
	XCopyPlane (dpy, XPIXMAP (p)->pixmap, x_win, gc, 0,
		    bitmap_y_offset < 0 ? 0 : bitmap_y_offset, width, height,
		    x, bitmap_y_offset < 0 ? y - bitmap_y_offset - a : y-a,
		    1L);
    }
  else if (SUBWINDOWP (p))
    {
      int sub_width;

      if ((x + XSUBWINDOW (p)->width) > (w->pixleft + w->pixwidth))
	sub_width = w->pixleft + w->pixwidth - x;
      else
	sub_width = XSUBWINDOW (p)->width;

      XMoveResizeWindow (dpy, XSUBWINDOW (p)->subwindow, x,
			 bitmap_y_offset < 0 ? y - bitmap_y_offset - a : y-a,
			 sub_width, XSUBWINDOW (p)->height);
      XMapWindow (dpy, XSUBWINDOW (p)->subwindow);
    }
  else
    abort();

  if (SOLID_CURSOR (cursor) && focus_cursor_p (s) && !NILP (Vbar_cursor))
    {
      int width = EQ (Vbar_cursor, Qt) ? 1 : 2;

      gcv.foreground = cursor_color;
      gcv.line_width = width;
      gc = gc_cache_lookup (the_gc_cache, &gcv, GCForeground | GCLineWidth);

      XDrawLine (dpy, x_win, gc, x+width-1, y-a, x+width-1, y+d-1);
    }
}

static void
#ifdef I18N4
ShipOutTextBlock (wchar_t *str, int count, int x, int y, int a,
		  int d, int cursor, struct face *face,
		  struct window_mirror *mir)
#else
ShipOutTextBlock (unsigned char *str, int count, int x, int y, int a,
		  int d, int cursor, struct face *face,
		  struct window_mirror *mir)
#endif
{
  struct window *w = XWINDOW (real_window (mir, 0));
  struct screen *s = XSCREEN (w->screen);
  Display *dpy = x_current_display;
  Window x_win = XtWindow (s->display.x->edit_widget);
  GC gc;
  XGCValues gcv;
  unsigned long text_mask;
  struct face *f;
  struct Lisp_Font *font;
  short wid;
  Bool clear_rect_p;
  Pixel cursor_color;

  memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
  gcv.graphics_exposures = False;

  if (count < 1) return;	/* allow calling with 0 counts */

  f = face ? face : &SCREEN_NORMAL_FACE(s);
  font = XFONT (FACE_FONT (f));

  BLOCK_INPUT;
  XtVaGetValues (s->display.x->edit_widget, XtNcursorColor, &cursor_color, 0);
  UNBLOCK_INPUT;

#ifdef I18N4
  wid = min (XwcTextEscapement (font->font, str, count),
	     (w->pixleft + w->pixwidth - x));
#else
  wid = min (XTextWidth (font->font, (char *) str, count),
	     (w->pixleft + w->pixwidth - x));
#endif

  /*
   * First we need to erase the area where the string is going to be
   * drawn.
   */
  clear_rect_p =
    ShipOutBlankBlock (False, wid, x, y, a, d, cursor, face, mir);

  /*
   * If the window is against the left border and the first character
   * has a left bearing then we need to explicitly clear that area.  We
   * just assume that every character has the bearing and clear if we
   * are against the border.
   */
  if (x == w->pixleft && !window_needs_vertical_divider (w))
    {
      unsigned short mod_a, line_height;

      line_height = a + d - 1;

      if (font->height < line_height)
	mod_a = font->height - d;
      else
	mod_a = a;

      if (SCREEN_INT_BORDER (s))
	XClearArea (dpy, x_win, 0, y-mod_a, SCREEN_INT_BORDER (s),
		    mod_a+d-1, 0);
    }

  text_mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
  gcv.foreground = FACE_FG_PIXEL (f);
  gcv.background = FACE_BG_PIXEL (f);
#ifndef I18N4
  gcv.font = font->font->fid;
#endif

  /* The focus cursor is done by drawing the character in its background
     on top of a background of the cursor color, unless Vbar_cursor is
     non-nil.  In that case it is a line drawn immediately before the
     character in the cursor color.
   */
  if (focus_cursor_p (s) && NILP (Vbar_cursor))
    {
      if (cursor == NORMAL_CURSOR)
	{
	  gcv.foreground = FACE_BG_PIXEL (f);
	  gcv.background = cursor_color;
	}
      else if (cursor == THIN_CURSOR)
	{
	  gcv.foreground = cursor_color;
	  gcv.background = FACE_BG_PIXEL (f);
	}
    }

  /* Now get the GC and draw the string. */
  gc = gc_cache_lookup (the_gc_cache, &gcv, text_mask);
  if (cursor != THIN_CURSOR)
    {
      if (clear_rect_p)
#ifdef I18N4
	XwcDrawString (dpy, x_win, font->font, gc, x, y, str, count);
#else
	XDrawString (dpy, x_win, gc, x, y, (char *) str, count);
#endif
      else
#ifdef I18N4
	XwcDrawImageString (dpy, x_win, font->font, gc, x, y, str, count);
#else
	XDrawImageString (dpy, x_win, gc, x, y, (char *) str, count);
#endif
    }
  else if (cursor == THIN_CURSOR && focus_cursor_p (s) && NILP (Vbar_cursor))
    {
      unsigned short mod_a, line_height;

      line_height = a + d - 1;
      if (font->height < line_height)
	mod_a = font->height - d;
      else
	mod_a = a;

      /* A thin cursor can only occur at eol where there is no character. */
      XFillRectangle (dpy, x_win, gc, x, y-mod_a, EOL_CURSOR_WIDTH, mod_a+d-1);
    }

  /* Draw underlining in same colors as the text.  We can use the same GC. */
  if (f->underline)
    {
      unsigned long upos;
      unsigned long uthick;
#ifdef I18N4 /* #### is this right? -jwz */
      upos = 0;
      uthick = 1;
#else /* ! I18N4 */
      if (!XGetFontProperty (font->font, XA_UNDERLINE_POSITION, &upos))
	upos = 0;
      if (!XGetFontProperty (font->font, XA_UNDERLINE_THICKNESS, &uthick))
	uthick = 1;
#endif /* ! I18N4 */
      if (uthick <= 1)
	XDrawLine (dpy, x_win, gc, x, y + upos, x + wid, y + upos);
      else
	XFillRectangle (dpy, x_win, gc, x, y + upos, wid, uthick);
    }

  if (SOLID_CURSOR (cursor) && focus_cursor_p (s) && !NILP (Vbar_cursor))
    {
      int width = EQ (Vbar_cursor, Qt) ? 1 : 2;

      gcv.foreground = cursor_color;
      gcv.line_width = width;
      gc = gc_cache_lookup (the_gc_cache, &gcv, GCForeground | GCLineWidth);

      XDrawLine (dpy, x_win, gc, x+width-1, y-a, x+width-1, y+d-1);
    }
  /* The non-focus cursor is done by drawing a rectangle around the character
     after it has been drawn normally.  We need a new GC for this, since the
     cursor color isn't necessarily the same as the foreground of the text.
   */
  else if (SOLID_CURSOR (cursor) && !focus_cursor_p (s))
    {
      gcv.foreground = cursor_color;
      gc = gc_cache_lookup (the_gc_cache, &gcv, GCForeground);

      if (NILP(Vbar_cursor))
	{
	  unsigned short mod_a, line_height;

	  line_height = a + d - 1;
	  if (font->height < line_height)
	    mod_a = font->height - d;
	  else
	    mod_a = a;

	  if (cursor == NORMAL_CURSOR)
	    XDrawRectangle (dpy, x_win, gc, x, y-mod_a, wid-1, mod_a+d-1);
	  else
	    /* Have to draw the non-focus EOL cursor slighly narrower
	       because XDrawRectangle apparently tacks on a little.  If
	       we don't adjust then we'll get droppings the next time
	       the cursor moves. */
	    XDrawRectangle (dpy, x_win, gc, x, y-mod_a, EOL_CURSOR_WIDTH - 1,
			    mod_a+d-1);
	}
    }
}


/*
 * Color an area.
 */
static void
XTcolor_area (struct screen *s, unsigned long color, int x, int y,
	      int width, int height)
{
  GC gc;
  XGCValues gcv;
  unsigned long bg_mask;

  if (!width || !height)
    return;

  BLOCK_INPUT;

  memset (&gcv, ~0, sizeof (XGCValues));
  gcv.graphics_exposures = False;
  gcv.foreground = color;
  gcv.fill_style = FillSolid;
  bg_mask = GCGraphicsExposures | GCForeground | GCFillStyle;
  gc = gc_cache_lookup (the_gc_cache, &gcv, bg_mask);

  XFillRectangle(x_current_display, XtWindow(s->display.x->edit_widget),
		 gc,x,y,width,height);

  UNBLOCK_INPUT;
}

/*
 * Clear region from ypos1 to ypos2, for entire window width
 */
static void
XTclear_window_end (struct window *w, int ypos1, int ypos2)
{
  struct screen *s = XSCREEN(w->screen);
  struct buffer *b = XBUFFER(w->buffer);
  int left_margin = LEFT_MARGIN(b,s,w);

  /* This is might be a sign that something is wrong in the internal
     redisplay algorithms.  And it might just be a side effect of
     clearing the bottom of a full screen.  It is definitely in code
     that is getting thrown away. */
  if (ypos2 < ypos1)
      return;

  BLOCK_INPUT;

  XClearArea (x_current_display, XtWindow(s->display.x->edit_widget),
	      w->pixleft + left_margin, ypos1,
	      w->pixwidth - left_margin, (ypos2 - ypos1), 0);
  if (!window_needs_vertical_divider (w) && !left_margin
      && SCREEN_INT_BORDER (s))
    XClearArea (x_current_display, XtWindow (s->display.x->edit_widget),
		0, ypos1, SCREEN_INT_BORDER (s), (ypos2 - ypos1), 0);
  XTcolor_area (s, FACE_BG_PIXEL (&SCREEN_LEFT_MARGIN_FACE(s)),
		w->pixleft, ypos1, left_margin, ypos2 - ypos1);
  UNBLOCK_INPUT;
}



/*
 * Shift region of lines according to scrolling info
 */
static void
XTshift_region (struct window *w, struct line_header *start,
		struct line_header *end)
{
  struct screen *s = XSCREEN(w->screen);
  register int old_top,new_top,length,i;
  int margin_pixwidth = LEFT_MARGIN (XBUFFER (w->buffer), s, w);
  int margin_pixleft = w->pixleft + margin_pixwidth;
  Window x_win = XtWindow (s->display.x->edit_widget);

  BLOCK_INPUT;
  
  old_top = start->prevy - start->ascent;
  new_top = start->ypos - start->ascent;
  length = end->ypos + end->descent - (start->ypos - start->ascent);

  if (length > 0 && old_top != new_top)
    {
      XGCValues gcv;
      GC copy_gc;

      memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
      gcv.graphics_exposures = False;
      copy_gc = gc_cache_lookup (the_gc_cache, &gcv, GCGraphicsExposures);
      XCopyArea (x_current_display,x_win, x_win, copy_gc,
                 w->pixleft, old_top,
                 w->pixwidth, length,
                 w->pixleft, new_top);

      if (new_top > old_top)
	{
	  /* Shifted region down */
	  length = new_top - old_top;
	  XClearArea (x_current_display, x_win,
                      margin_pixleft, old_top,
                      w->pixwidth - margin_pixwidth, length, 0);
	  if (margin_pixwidth)
	    XTcolor_area (s, FACE_BG_PIXEL (&SCREEN_LEFT_MARGIN_FACE(s)),
			  w->pixleft, old_top,
			  margin_pixwidth, length);
	  else if (!window_needs_vertical_divider (w) && SCREEN_INT_BORDER(s))
	    XClearArea (x_current_display, x_win, 0, old_top,
			SCREEN_INT_BORDER (s), length, 0);	    
	}
      else
	{
	  /* Shifted region up */
	  i = end->ypos + end->descent;
	  length = old_top - new_top;
	  XClearArea (x_current_display, x_win,
                      margin_pixleft,i,
                      w->pixwidth - margin_pixwidth, length, 0);
	  if (margin_pixwidth)
	    XTcolor_area (s, FACE_BG_PIXEL (&SCREEN_LEFT_MARGIN_FACE(s)),
			  w->pixleft,i,
			  margin_pixwidth, length);
	  else if (!window_needs_vertical_divider (w) && SCREEN_INT_BORDER(s))
	    XClearArea (x_current_display, x_win, 0, new_top,
			SCREEN_INT_BORDER (s), i - new_top, 0);	    
	}
    }

  /* We need to scan for any subwindows that were in the shifted region
     and move them to where they should be. */
  {
    struct line_header *l = start;

    while (l != end->next)
      {
	if (l->subwindow)
	  {
	    struct char_block *cb, *end_cb;
	    int loop;

	    for (loop = 0; loop < 2; loop++);
	    {
	      if (loop)
		{
		  cb = l->body;
		  end_cb = l->end;
		}
	      else
		{
		  cb = l->margin_start;
		  end_cb = l->margin_end;
		}

	    while (cb && cb != end_cb)
	      {
		if (!cb->char_b && SUBWINDOWP (glyph_to_pixmap (cb->glyph)))
		  {
		    Lisp_Object sw = glyph_to_pixmap (cb->glyph);
		    int height = XSUBWINDOW (sw)->height;
		    int y_offset;
		    int width;

		    y_offset = height - (l->ascent + l->descent);
		    y_offset /= 2;
		    if ((cb->xpos + XSUBWINDOW (sw)->width) >
			(w->pixleft + w->pixwidth))
		      width = w->pixleft + w->pixwidth - cb->xpos;
		    else
		      width = XSUBWINDOW (sw)->width;

		    XMoveResizeWindow (x_current_display,
				       XSUBWINDOW (sw)->subwindow,
				       cb->xpos,
				       (y_offset < 0
					? l->ypos - y_offset - l->ascent
					: l->ypos - l->ascent),
				       width, height);
		    XMapWindow (x_current_display, XSUBWINDOW (sw)->subwindow);
		  }
		cb = cb->next;
	      }
	    }
	  }
	l = l->next;
      }
  }

  UNBLOCK_INPUT;
}



/*
 * Fast insert for char in middle of line.
 * ASSUMPTIONS:  Remaining chars on line will be blt'ed.  No region needs 
 *		 to be cleared at end of line.
 */
static void
InsertChar (struct window *w, struct line_header *l, struct char_block *new,
	    struct char_block *cb, struct char_block *end, char clear)
{
  struct screen *s = XSCREEN(w->screen);
  Window x_win = XtWindow (s->display.x->edit_widget);
#ifdef I18N4
  wchar_t b[2];
#else
  unsigned char b[2];
#endif
  XGCValues gcv;
  GC copy_gc;

  b[1] = 0;
  b[0] = new->ch;

  memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
  gcv.graphics_exposures = False;
  copy_gc = gc_cache_lookup (the_gc_cache, &gcv, GCGraphicsExposures);

  BLOCK_INPUT;
  XCopyArea(x_current_display, x_win, x_win, copy_gc,
	    new->xpos,			/* start x */
	    l->ypos - l->ascent, 	/* start y */
	    end->xpos - cb->xpos,	/* width */
	    l->ascent + l->descent,	/* height */
	    cb->xpos,
	    l->ypos - l->ascent);

  ShipOutTextBlock(b,1,new->xpos,l->ypos,l->ascent,l->descent,0,
		      new->face, find_window_mirror (w));
  UNBLOCK_INPUT;  
  return;
}



/*
 * Real fcn to return width of text string displayed in FONT when under X.
 */
#ifdef I18N4
static int
XTtext_width (Lisp_Object f, CONST wchar_t *s, int len)
{
  return (XwcTextEscapement (XFONT(f)->font, (wchar_t *) s, len));
}
#else
static int
XTtext_width (Lisp_Object f, CONST unsigned char *s, int len)
{
  return (XTextWidth (XFONT(f)->font, (char *) s, len));
}
#endif


static void
XTclear_screen ()
{
  struct screen *s;
  struct window *w;

  if (updating_screen == 0)
    s = selected_screen;
  else
    s = updating_screen;
  w = XWINDOW (s->selected_window);

  s->cursor_x = s->cursor_y = 0;
  s->new_cur_line = s->cur_line = window_display_lines (w);
  s->new_cur_char = s->cur_char = window_display_lines (w)->body;
  s->phys_cursor_x = -1;
  BLOCK_INPUT;
  XClearWindow (x_current_display, XtWindow (s->display.x->edit_widget));
  CursorToggle (s);
  if (!updating_screen)
    XFlushQueue();
  UNBLOCK_INPUT;
}

/* briefly swap the foreground and background colors.
 */

extern int select ();

static void
XTflash (s)
     struct screen *s;
{
  struct face *face;
  Display *dpy;
  Window w;
  XGCValues gcv;
  GC gc;
  Widget shell = s->display.x->widget;
  Dimension width, height;

  if (updating_screen != 0)
    return;

  BLOCK_INPUT;
  XtVaGetValues (shell, XtNwidth, &width, XtNheight, &height, 0);
  UNBLOCK_INPUT;

  face = &SCREEN_NORMAL_FACE (s);
  dpy = XtDisplay (shell);
  w = XtWindow (s->display.x->edit_widget);
  memset (&gcv, ~0, sizeof (XGCValues)); /* initialize all slots to ~0 */
  gcv.foreground = (FACE_FG_PIXEL (face) ^ FACE_BG_PIXEL (face));
  gcv.function = GXxor;
  gcv.graphics_exposures = False;
  gc = gc_cache_lookup (the_gc_cache, &gcv,
			(GCForeground | GCFunction | GCGraphicsExposures));
  BLOCK_INPUT;
  XFillRectangle (dpy, w, gc, 0, 0, width, height);
  XSync (dpy, False);
  UNBLOCK_INPUT;

  {
    int usecs = 100000;
    struct timeval tv;
    tv.tv_sec  = usecs / 1000000L;
    tv.tv_usec = usecs % 1000000L;
    /* I'm sure someone is going to complain about this... */
    (void) select (0, 0, 0, 0, &tv);
  }

  BLOCK_INPUT;
  XFillRectangle (dpy, w, gc, 0, 0, width, height);
  XSync (dpy, False);
  UNBLOCK_INPUT;
}

/* Make audible bell.  */

/* X defines volume as from -100 to 100; we use 0 to 100 */
extern Lisp_Object Vbell_volume;

static void x_hairy_beep (Display * dpy,
			  int volume, int pitch, int duration);


static void
XTsimple_beep (int volume, int pitch, int duration)
{
  if (volume < 0) volume = 0;
  else if (volume > 100) volume = 100;
  if (pitch < 0 && duration < 0)
    {
      BLOCK_INPUT;
      XBell (x_current_display, (volume * 2) - 100);
      XFlushQueue ();
      UNBLOCK_INPUT;
    }
  else
    {
      x_hairy_beep (x_current_display, (volume * 2) - 100, pitch, duration);
    }

}

static void
XTring_bell (sound)
     Lisp_Object sound;
{
  if (visible_bell)
    XTflash (selected_screen);
  else
    Fplay_sound (sound, Qnil);
}

static void
x_hairy_beep (Display *dpy, int volume, int pitch, int duration)
     /* volume is -100 to 100 (the X way) instead of 0-100 (the emacs way) */
{
  XKeyboardState state;
  XKeyboardControl ctl;
  BLOCK_INPUT;
  XSync (dpy, 0);
  /* #### grab server? */
  XGetKeyboardControl (dpy, &state);

  ctl.bell_pitch    = (pitch    >= 0 ? pitch    : state.bell_pitch);
  ctl.bell_duration = (duration >= 0 ? duration : state.bell_duration);
  XChangeKeyboardControl (dpy, KBBellPitch|KBBellDuration, &ctl);

  XBell (dpy, volume);

  ctl.bell_pitch    = state.bell_pitch;
  ctl.bell_duration = state.bell_duration;
  XChangeKeyboardControl (dpy, KBBellPitch|KBBellDuration, &ctl);

  /* #### ungrab server? */
  XSync (dpy, 0);
  UNBLOCK_INPUT;
}


/* Specify how many text lines, from the top of the window,
   should be affected by insert-lines and delete-lines operations.
   This, and those operations, are used only within an update
   that is bounded by calls to XTupdate_begin and XTupdate_end.  */

static void
XTset_terminal_window (n)
     int n;
{
  if (updating_screen == 0)
    /* abort ();   fuck this */
    return;

  if ((n <= 0) || (n > updating_screen->height))
    update_height = updating_screen->height;
  else
    update_height = n;
}

/* Make SCREEN the current input screen.  */
void
x_new_selected_screen (screen)
     struct screen *screen;
{
  struct screen* previous_screen = selected_screen;

  if (previous_screen != screen)
    {
      selected_screen = screen;

#if 1
      if (previous_screen && previous_screen != XSCREEN(Vterminal_screen))
	Fastmove_cursor (previous_screen);
#endif
      update_cursor (screen, 1);
    }
}


/************************************************************************/
/*				handle X errors				*/
/************************************************************************/

static CONST char *events[] =
{
   "0: ERROR!",
   "1: REPLY",
   "KeyPress",
   "KeyRelease",
   "ButtonPress",
   "ButtonRelease",
   "MotionNotify",
   "EnterNotify",
   "LeaveNotify",
   "FocusIn",
   "FocusOut",
   "KeymapNotify",
   "Expose",
   "GraphicsExpose",
   "NoExpose",
   "VisibilityNotify",
   "CreateNotify",
   "DestroyNotify",
   "UnmapNotify",
   "MapNotify",
   "MapRequest",
   "ReparentNotify",
   "ConfigureNotify",
   "ConfigureRequest",
   "GravityNotify",
   "ResizeRequest",
   "CirculateNotify",
   "CirculateRequest",
   "PropertyNotify",
   "SelectionClear",
   "SelectionRequest",
   "SelectionNotify",
   "ColormapNotify",
   "ClientMessage",
   "MappingNotify",
   "LASTEvent"
};

CONST char *
x_event_name (event_type)
     int event_type;
{
  if (event_type < 0) return 0;
  if (event_type >= (sizeof (events) / sizeof (char *))) return 0;
  return events [event_type];
}

/* Handling errors.

   If an X error occurs which we are not expecting, we have no alternative
   but to print it to stderr.  It would be nice to stuff it into a pop-up
   buffer, or to print it in the minibuffer, but that's not possible, because
   one is not allowed to do any I/O on the display connection from an error
   handler. The guts of Xlib expect these functions to either return or exit.


   ####  The following is pretty dubious; I'm no longer sure it's worth the
   ####  effort, but I'm not going to delete the code just yet...

   However, there are occasions when we might expect an error to reasonably
   occur.  The interface to this is as follows:

   Before calling some X routine which may error, call
	expect_x_error (dpy);

   Just after calling the X routine, call either:

	x_error_occurred_p (dpy);

   to ask whether an error happened (and was ignored), or:

	signal_if_x_error (dpy, resumable_p);

   which will call Fsignal() with args appropriate to the X error, if there
   was one.  (Resumable_p is whether the debugger should be allowed to
   continue from the call to signal.)

   You must call one of these two routines immediately after calling the X
   routine; think of them as bookends like BLOCK_INPUT and UNBLOCK_INPUT.
 */

#ifdef DUBIOUS_X_ERROR_HANDLING
static int error_expected;
static int error_occurred;
static XErrorEvent last_error;
#endif

extern Lisp_Object Qdelete_screen;

/* OVERKILL! */

#ifdef EXTERNAL_WIDGET
static Lisp_Object
x_error_handler_do_enqueue (Lisp_Object screen)
{
  Fenqueue_command_event (Qdelete_screen, screen);
  return Qt;
}

static Lisp_Object
x_error_handler_error (Lisp_Object data, Lisp_Object dummy)
{
  return Qnil;
}
#endif /* EXTERNAL_WIDGET */

static int
x_error_handler (disp, event)
     Display *disp;
     XErrorEvent *event;
{
#ifdef DUBIOUS_X_ERROR_HANDLING
  if (error_expected)
    {
      error_expected = 0;
      error_occurred = 1;
      last_error = *event;
    }
  else
#endif
    {
#ifdef EXTERNAL_WIDGET
      struct screen *s;

      if ((event->error_code == BadWindow ||
	   event->error_code == BadDrawable)
	  && ((s = x_any_window_to_screen (event->resourceid)) != 0))
	{
	  Lisp_Object screen;

	/* one of the windows comprising one of our screens has died.
	   This occurs particularly with ExternalShell screens when the
	   client that owns the ExternalShell's window dies.

	   We cannot do any I/O on the display connection so we need
	   to enqueue a command event so that the deletion happens
	   later.

	   Furthermore, we need to trap any errors (out-of-memory) that
	   may occur when Fenqueue_command_event is called.
	 */

	if (s->being_deleted)
	  return 0;
	XSETR (screen, Lisp_Screen, s);
	if (!NILP (condition_case_1 (Qerror, x_error_handler_do_enqueue,
				     screen, x_error_handler_error, Qnil)))
	  {
	    s->being_deleted = 1;
	    s->visible = 0;
	  }
	return 0;
      }
#endif /* EXTERNAL_WIDGET */

      fprintf (stderr, "\n%s: ",
	       (STRINGP (Vinvocation_name)
		? (char *) XSTRING (Vinvocation_name)->data
		: "xemacs"));
      XmuPrintDefaultErrorMessage (disp, event, stderr);
    }
  return 0;
}


#ifdef DUBIOUS_X_ERROR_HANDLING

void
expect_x_error (Display *dpy)
{
  if (error_expected) abort ();
  XSync (dpy, 0);	/* handle pending errors before setting flag */
  error_expected = 1;
  error_occurred = 0;
}

int
x_error_occurred_p (Display *dpy)
{
  int val;
  XSync (dpy, 0);	/* handle pending errors before setting flag */
  val = error_occurred;
  error_expected = 0;
  error_occurred = 0;
  return val;
}

int
signal_if_x_error (Display *dpy, int resumable_p)
{
  char buf [1024];
  Lisp_Object data;
  if (! x_error_occurred_p (dpy))
    return 0;
  data = Qnil;
  sprintf (buf, "0x%X", (unsigned int) last_error.resourceid);
  data = Fcons (build_string (buf), data);
  {
    char num [32];
    sprintf (num, "%d", last_error.request_code);
    XGetErrorDatabaseText (last_error.display, "XRequest", num, "",
			   buf, sizeof (buf));
    if (! *buf)
      sprintf (buf, "Request-%d", last_error.request_code);
    data = Fcons (build_string (buf), data);
  }
  XGetErrorText (last_error.display, last_error.error_code, buf, sizeof (buf));
  data = Fcons (build_string (buf), data);
 again:
  Fsignal (Qx_error, data);
  if (! resumable_p) goto again;
  return 1;
}
#endif

/* This is called when the display connection becomes hosed.
   It may not return.
 */
static int
x_IO_error_handler (disp)
     Display *disp;
{
  fprintf (stderr, GETTEXT ("\n%s: Fatal I/O Error %d (%s) on display connection \"%s\"\n"),
	   (STRINGP (Vinvocation_name)
	    ? (char *) XSTRING (Vinvocation_name)->data
	    : "xemacs"),
	   errno, strerror (errno), DisplayString (disp));
  fprintf (stderr,
      GETTEXT ("  after %lu requests (%lu known processed) with %d events remaining.\n"),
	   NextRequest (disp) - 1, LastKnownRequestProcessed (disp),
	   QLength(disp));
  if (_Xdebug)
    abort ();
  else
    {
      fprintf (stderr, GETTEXT ("  Autosaving and exiting...\n"));
      x_current_display = 0; /* it's dead! */
      Vwindow_system = Qnil; /* let it lie! */
      Fset (Qkill_emacs_hook, Qnil); /* too dangerous */
      Fkill_emacs (make_number (70));
    }
  return 0; /* not reached; suppress warnings */
}



/* Pixmap cache */

static Lisp_Object Vxemacs_logo;
static Lisp_Object Vcontinuer_glyph, Vtruncator_glyph, Vrarrow_glyph;
GLYPH continuer_glyph, truncator_glyph;
static GLYPH rarrow_glyph, xemacs_glyph;


/* To reduce memory usage, the things that use glyphs (extents and the
   redisplay structures) use GLYPHs, which are 16 bit ids until the
   last minute.

   When an extent is marked, it turns a GLYPH into the underlying lisp
   object, and marks that.  (#### NOTE: redisplay must do the same!)

   The association of GLYPHs to objects happens in a weak hash table,
   so that entries drop out of this table when the table holds the only
   pointer.
 */

/* The last-allocated glyph ID.  If this wraps we're in big trouble. */
static GLYPH last_glyph_id;

/* Weak hashtables, hashing GLYPH ids to/from pixmaps, strings, etc. */
static Lisp_Object Vglyph_id_to_glyph_table;
static Lisp_Object Vglyph_to_glyph_id_table;

Lisp_Object
glyph_to_pixmap (GLYPH g)
{
  Lisp_Object p;
  /* Special-case the common ones. */
  if (g == continuer_glyph) return Vcontinuer_glyph;
  else if (g == truncator_glyph) return Vtruncator_glyph;
  else if (g == rarrow_glyph) return Vrarrow_glyph;
  else if (g >= (GLYPH) last_glyph_id)
    /* abort (); */
    return Qnil; /* #### KLUDGE */
  p = Fgethash (make_number (g), Vglyph_id_to_glyph_table, Qnil);
  if (!gc_in_progress && /* yeah, yeah... */
      !NILP (p) && !PIXMAPP (p) && !STRINGP (p) && !SUBWINDOWP (p))
    abort ();
  return p;
}


GLYPH
pixmap_to_glyph (Lisp_Object pixmap)
{
  GLYPH g;
  Lisp_Object id;
  if (!PIXMAPP(pixmap) && !STRINGP(pixmap) && !SUBWINDOWP(pixmap))
    abort ();
  id = Fgethash (pixmap, Vglyph_to_glyph_id_table, Qnil);

  if (!NILP (id))
    {
      g = XINT (id);
      if (g <= 0 || g >= last_glyph_id)
	abort ();
    }
  else
    {
      g = last_glyph_id++;
      id = make_number (g);
      Fputhash (pixmap, id, Vglyph_to_glyph_id_table);
      Fputhash (id, pixmap, Vglyph_id_to_glyph_table);
    }
  return g;
}


/*
 * Mark all subwindows belonging to given screen with the given flag.
 */
void
mark_subwindow_display_status (struct screen *s, int flag)
{
  GLYPH i;
  /* #### should use maphash instead! */
  for (i = 0; i < last_glyph_id; i++)
    {
      Lisp_Object sw = glyph_to_pixmap (i);

      if (SUBWINDOWP (sw) && (s == XSCREEN (XSUBWINDOW (sw)->screen)))
	XSUBWINDOW (sw)->being_displayed = flag;
    }
}

/*
 * Unmap all subwindows belonging to the given screen which aren't marked
 */
void
unmap_unmarked_subwindows_of_screen (struct screen *s)
{
  GLYPH i;
  /* #### should use maphash instead! */
  for (i = 0; i < last_glyph_id; i++)
    {
      Lisp_Object sw = glyph_to_pixmap (i);

      if (SUBWINDOWP (sw) && (s == XSCREEN (XSUBWINDOW (sw)->screen)))
	if (!XSUBWINDOW (sw)->being_displayed)
	  XUnmapWindow (x_current_display, XSUBWINDOW (sw)->subwindow);
    }
}


void 
init_bitmap_tables_1 ()
{
  last_glyph_id = 1;	/* don't use 0 */
  Vglyph_id_to_glyph_table = make_weak_hashtable (50, 0, 0);
  Vglyph_to_glyph_id_table = make_weak_hashtable (50, 0, 0);
  staticpro (&Vglyph_id_to_glyph_table);
  staticpro (&Vglyph_to_glyph_id_table);
  staticpro (&Vcontinuer_glyph); Vcontinuer_glyph = Qnil;
  staticpro (&Vtruncator_glyph); Vtruncator_glyph = Qnil;
  staticpro (&Vrarrow_glyph);    Vrarrow_glyph = Qnil;
  DEFVAR_LISP ("xemacs-logo", &Vxemacs_logo, 0);
  Vxemacs_logo = Qnil;
}

static void
init_bitmap_tables ()
{
  Display *dpy = x_current_display;
  Screen *screen = DefaultScreenOfDisplay (dpy);
  Lisp_Object p;
#define Vxemacs_glyph Vxemacs_logo /* kludge... */

#define MAKE_BUILTIN(NAME, str_name)					\
  NAME##_glyph = last_glyph_id++;					\
  p = make_pixmap_from_data (screen, (char *) NAME##_bits,		\
			     NAME##_width, NAME##_height);		\
  XPIXMAP (p)->file_name = build_string ((str_name));			\
  V##NAME##_glyph = p;							\
  Fputhash (make_number (NAME##_glyph), p, Vglyph_id_to_glyph_table);	\
  Fputhash (p, make_number (NAME##_glyph), Vglyph_to_glyph_id_table);

  MAKE_BUILTIN (continuer, "builtin_continuer");
  MAKE_BUILTIN (truncator, "builtin_truncator");
  MAKE_BUILTIN (rarrow, "builtin_rarrow");
  MAKE_BUILTIN (xemacs, "builtin_logo");
#undef MAKE_BUILTIN
}



unsigned short
glyph_width (GLYPH g, Lisp_Object font)
{
  Lisp_Object p = glyph_to_pixmap (g);

  if (PIXMAPP (p))
    {
      return XPIXMAP (p)->width;
    }
  else if (STRINGP (p))
    {
#ifdef I18N4
      safe_mbstowcs ((char *) XSTRING (p)->data, &wc_buf);
      return text_width (font, wc_buf.data, wc_buf.in_use + 1);
#else
      return text_width (font, XSTRING (p)->data, XSTRING (p)->size);
#endif
    }
  else if (SUBWINDOWP (p))
    {
      return (unsigned short) XSUBWINDOW (p)->width;
    }
  else
    /* An unitialized glyph can cause this to be reached from the cursor
       code.  So let's be a little more civil than just aborting. */
    /* #### Hey!  Let's be a little more careful about using uninitialized
       memory instead! */
    /* #### Hey!  the memory IS intialized.  It just isn't set to anything
       useful.  It is a timing problem (i.e. hard to track down). */
    return 0;
}

unsigned short
glyph_height (GLYPH g, Lisp_Object font)
{
  Lisp_Object p = glyph_to_pixmap (g);

  if (PIXMAPP (p))
    return XPIXMAP (p)->height;
  else if (STRINGP (p))
    return (XFONT (font)->ascent + XFONT (font)->descent);
  else if (SUBWINDOWP (p))
    return (unsigned short) XSUBWINDOW (p)->height;
  else
    abort ();
}


/************************************************************************/
/*			initializing the X connection			*/
/************************************************************************/

void
make_argc_argv (argv_list, argc, argv)
     Lisp_Object argv_list;
     int* argc;
     char*** argv;
{
  Lisp_Object next;
  int n = XINT (Flength (argv_list));
  int i;
  *argv = (char**)xmalloc ((n+1) * sizeof (char*));

  for (i = 0, next = argv_list; i < n; i++, next = XCONS (next)->cdr)
    {
      CHECK_STRING (XCONS (next)->car, 0);
      (*argv) [i] = (char*) (XSTRING (XCONS (next)->car)->data);
    }
  (*argv) [n] = 0;
  *argc = i;
}

Lisp_Object
make_arg_list (argc, argv)
     int argc;
     char** argv;
{
  Lisp_Object result;
  int i;

  for (i = 0, result = Qnil; i < argc; i++)
    result = Fcons (build_string (argv [i]), result);
  return Fnreverse (result);
}

static XrmOptionDescRec emacs_options[] = {
  {"-geometry",	".geometry", XrmoptionSepArg, NULL},
  {"-iconic",	".iconic", XrmoptionNoArg, (XtPointer) "yes"},

  {"-internal-border-width", "*EmacsScreen.internalBorderWidth",
     XrmoptionSepArg, NULL},
  {"-ib",	"*EmacsScreen.internalBorderWidth", XrmoptionSepArg, NULL},
  {"-scrollbar-width", "*EmacsScreen.scrollBarWidth", XrmoptionSepArg, NULL},

  /* #### Beware!  If the type of the shell changes, update this. */
  {"-T",	"*EmacsShell.title", XrmoptionSepArg, (XtPointer) NULL},
  {"-wn",	"*EmacsShell.title", XrmoptionSepArg, (XtPointer) NULL},
  {"-title",	"*EmacsShell.title", XrmoptionSepArg, (XtPointer) NULL},
  {"-iconname",	"*EmacsShell.iconName", XrmoptionSepArg, (XtPointer) NULL},
  {"-in",	"*EmacsShell.iconName", XrmoptionSepArg, (XtPointer) NULL},
  {"-mc",	"*pointerColor", XrmoptionSepArg, (XtPointer) NULL},
  {"-cr",	"*cursorColor", XrmoptionSepArg, (XtPointer) NULL}
};


static void sanity_check_geometry_resource (Display *);
extern void x_init_modifier_mapping (Display *);

extern Lisp_Object Vx_emacs_application_class;
extern int x_selection_timeout;

#ifdef I18N4
extern void init_input (CONST char *res_name, CONST char *res_class);
#endif

Lisp_Object
x_term_init (argv_list)
     Lisp_Object argv_list;
{
  int argc;
  char** argv;
  char *app_class;
#ifdef F_SETOWN
  extern int old_fcntl_owner;
#endif

  if (STRINGP (Vx_emacs_application_class) &&
      XSTRING (Vx_emacs_application_class)->size > 0)
    app_class = (char *) XSTRING (Vx_emacs_application_class)->data;
  else
    app_class = (char *) "Emacs";

  make_argc_argv (argv_list, &argc, &argv);

#ifdef DUBIOUS_X_ERROR_HANDLING
  error_expected = 0;
  error_occurred = 0;
#endif

  Xt_app_shell = XtAppInitialize (&Xt_app_con, app_class,
				  emacs_options, XtNumber(emacs_options),
				  &argc, argv,
				  x_fallback_resources, NULL, 0);
  x_current_display = XtDisplay (Xt_app_shell);

  if (x_current_display == 0)
    fatal (GETTEXT ("X server \"%s\" not responding\n"), XDisplayName (0));

  x_file_descriptor = ConnectionNumber (x_current_display);

  sanity_check_geometry_resource (x_current_display);

  /* In event-Xt.c */
  x_init_modifier_mapping (x_current_display);
  
  /* In xselect.c */
  x_selection_timeout = (XtAppGetSelectionTimeout (Xt_app_con) / 1000);

  {
#if defined (USG) && !defined (HAVE_GETHOSTNAME)
    static struct utsname get_system_name_name;
#endif /* USG && !HAVE_GETHOSTNAME */
    char tem_name[MAXPATHLEN];
    int l;

#if defined (USG) && !defined (HAVE_GETHOSTNAME)
    uname (&get_system_name_name);
    strcpy (tem_name, get_system_name_name.nodename);
#else /* not USG && !HAVE_GETHOSTNAME */
    gethostname (tem_name, MAXPATHLEN - 1);
#endif /* not USG */
    tem_name[99] = 0;
    if ((l = strlen (tem_name)) < MAXPATHLEN -1)
      {
	hostname = (char *) xmalloc (l+1);
	memcpy (hostname, tem_name, l + 1);
      }
    else
      {
	hostname = (char *) xmalloc (10);
	memcpy (hostname, GETTEXT ("Somewhere"), 9);
	hostname[10] = 0;
      }
    id_name = (char *) xmalloc (string_length (XSTRING (Vinvocation_name))
                                + strlen (hostname) + 2);
    sprintf (id_name, "%s@%s", XSTRING (Vinvocation_name)->data, hostname);
  }
  

#ifdef HPUX
  {
    int owner = getpid ();
    ioctl (x_file_descriptor, SIOCSPGRP, &owner);
  }
#else /* !HPUX */
#ifdef F_SETOWN
  old_fcntl_owner = fcntl (x_file_descriptor, F_GETOWN, 0);
#ifdef F_SETOWN_SOCK_NEG
  /* stdin is a socket here */
  fcntl (x_file_descriptor, F_SETOWN, -getpid ());
#else
  fcntl (x_file_descriptor, F_SETOWN, getpid ());
#endif /* F_SETOWN_SOCK_NEG */
#endif /* F_SETOWN */
#endif /* !HPUX */

#ifdef SIGIO
  init_sigio ();
#endif

  /* Must use interrupt input because we cannot otherwise
     arrange for C-g to be noticed immediately.
     We cannot connect it to SIGINT.  */
  Fset_input_mode (Qt, Qnil, Qt);

  clear_window_end_hook = XTclear_window_end;
  shift_region_hook = XTshift_region;

  update_line_hook = PlotTextLine;
  insert_chars_hook = InsertChar;
  text_width_hook = XTtext_width;

  clear_screen_hook = XTclear_screen;
  ring_bell_hook = XTring_bell;
  beep_hook = XTsimple_beep;
  update_begin_hook = XTupdate_begin;
  update_end_hook = XTupdate_end;
  set_terminal_window_hook = XTset_terminal_window;

  /* read_socket_hook = (Lisp_Object (*)())-1; */
  cursor_to_hook = XTcursor_to;

  scroll_region_ok = 1;		/* we'll scroll partial screens */
  char_ins_del_ok = 0;		/* just as fast to write the line */
  line_ins_del_ok = 1;		/* we'll just blt 'em */
  fast_clear_end_of_line = 1;	/* X does this well */
  memory_below_screen = 0;	/* we don't remember what scrolls 
				   off the bottom */
  baud_rate = 19200;
  x_interline_space = 0;

/*  Vlast_bar_cursor = Qnil;
   staticpro (&Vlast_bar_cursor); */

  init_bitmap_tables ();

  XSetErrorHandler (x_error_handler);
  XSetIOErrorHandler (x_IO_error_handler);

  emacs_Xt_make_event_stream ();

#ifdef I18N4
  init_input (*argv, app_class);
#endif

  /* Disable Window Change signals;  they are handled by X events. */
#ifdef SIGWINCH
  signal (SIGWINCH, SIG_IGN);
#endif /* SIGWINCH */

  {
    Lisp_Object L = make_arg_list (argc, argv);
    xfree (argv);
    return L;
  }
}

static void
sanity_check_geometry_resource (Display *dpy)
{
  char *app_name, *app_class, *s;
  char buf1 [255], buf2 [255];
  char *type;
  XrmValue value;
  XtGetApplicationNameAndClass (dpy, &app_name, &app_class);
  strcpy (buf1, app_name);
  strcpy (buf2, app_class);
  for (s = buf1; *s; s++) if (*s == '.') *s = '_';
  strcat (buf1, "._no_._such_._resource_.geometry");
  strcat (buf2, "._no_._such_._resource_.Geometry");
  if (XrmGetResource (XtDatabase (dpy), buf1, buf2, &type, &value) == True)
    {
      fprintf (stderr, GETTEXT ("\n\
Apparently \"%s*geometry: %s\" or \"%s*geometry: %s\" was\n\
specified in the resource database.  Specifying \"*geometry\" will make\n\
emacs (and most other X programs) malfunction in obscure ways.  You\n\
should always use \".geometry\" instead.\n\n"),
	       app_name, (char *) value.addr,
	       app_class, (char *) value.addr);
      exit (-1);
    }
}



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

void
x_set_offset (s, xoff, yoff)
     struct screen *s;
     int xoff, yoff;
{
  Widget w = s->display.x->widget;
  Display *dpy = XtDisplay (w);
  Dimension screen_w = DisplayWidth (dpy, DefaultScreen (dpy));
  Dimension screen_h = DisplayHeight (dpy, DefaultScreen (dpy));
  Dimension shell_w, shell_h, shell_bord;
  int win_gravity;

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

  win_gravity =
    xoff >= 0 && yoff >= 0 ? NorthWestGravity :
    xoff >= 0 ? SouthWestGravity :
    yoff >= 0 ? NorthEastGravity :
    SouthEastGravity;
  if (xoff < 0)
    xoff += screen_w - shell_w - 2*shell_bord;
  if (yoff < 0)
    yoff += screen_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). */
  BLOCK_INPUT;
  XtVaSetValues (w,
		 XtNwinGravity, win_gravity,
		 XtNx, xoff,
		 XtNy, yoff,
		 0);
  /* Sometimes you will find that

     (set-screen-position (selected-screen) -50 -50)

     doesn't put the screen 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
  UNBLOCK_INPUT;
}

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

void
x_set_window_size (s, cols, rows)
     struct screen *s;
     int cols, rows;
{
  BLOCK_INPUT;
  EmacsScreenSetCharSize (s->display.x->edit_widget, cols, rows);
#if 0
  {
    Widget w = s->display.x->widget;
    /* this is not correct.  x_set_window_size() is called from
       Fset_screen_size(), which may or may not have been called
       by the user (e.g. update_EmacsScreen() calls it when the font
       changes).  For now, don't bother with getting this right. */
    x_wm_mark_shell_size_user_specified (w);
  }
#endif
  UNBLOCK_INPUT;
}



void
x_set_mouse_position (s, x, y)
     struct screen *s;
     int x, y;
{
  struct Lisp_Font *font = XFONT (SCREEN_DEFAULT_FONT (s));

  x = SCREEN_INT_BORDER(s) + x * font->width + font->width / 2;
  y = SCREEN_INT_BORDER(s) + y * font->height + font->height / 2;

  BLOCK_INPUT;
  XWarpPointer (x_current_display, None, XtWindow(s->display.x->edit_widget),
		0, 0, 0, 0, x, y);
  UNBLOCK_INPUT;
}

void
x_read_mouse_position (s, x, y)
     struct screen *s;
     int *x, *y;
{
  Window w;
  int ix, iy;
  int ibw = SCREEN_INT_BORDER (s);
  Window root_window;
  int root_x, root_y;
  unsigned int keys_and_buttons;

  BLOCK_INPUT;
  if (XQueryPointer (x_current_display, XtWindow (s->display.x->edit_widget),
       	      &root_window, &w, &root_x, &root_y, &ix, &iy,
       	      &keys_and_buttons) == False)
    {
      UNBLOCK_INPUT;
      error (GETTEXT ("Pointer not on same screen as window."));
    }
  UNBLOCK_INPUT;

  *x = (ix - ibw) / (int) XFONT (SCREEN_DEFAULT_FONT (s))->width;
  *y = (iy - ibw) / ((int) XFONT (SCREEN_DEFAULT_FONT (s))->height +
       x_interline_space);
}




static int
should_raise_by_send_event;

static int
handle_raise_error (Display* display, XErrorEvent* event)
{
  should_raise_by_send_event = 1;
  return 0;
}

/* Raise screen S.  */
void
x_raise_screen (s, force)
     struct screen *s;
     int force;
{
  Widget bottom_dialog;
  Window emacs_window;
  XWindowChanges xwc;
  int flags;
  int (*prev_handler) (Display*, XErrorEvent*);

  BLOCK_INPUT;
  if (s->visible || force)
    {
      emacs_window = XtWindow (s->display.x->widget);
      /* 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;
	}

      /* #### the junk below should be replaced by a call to
	 XReconfigreWMWindow() */

      /* get ready to handle the error generated by XConfigureWindow */
      XSync (x_current_display, False);
      should_raise_by_send_event = 0;
      prev_handler = XSetErrorHandler (handle_raise_error);
      /* first try the plain configure notify and detect an error */
      XConfigureWindow (x_current_display, emacs_window, flags, &xwc);

      /* try to see if some error is generated */
      XSync (x_current_display, False);
      XSetErrorHandler (prev_handler);
      if (should_raise_by_send_event)
	{
	  XConfigureRequestEvent ev;
	  Window root = RootWindow (x_current_display,
				    DefaultScreen (x_current_display));
	  ev.type = ConfigureRequest;
	  ev.parent = root;
	  ev.window = emacs_window;
	  ev.above = XtWindow (bottom_dialog);
	  ev.value_mask = flags;
	  XSendEvent (x_current_display, root, False,
		      SubstructureRedirectMask|SubstructureNotifyMask,
		      (XEvent *)&ev);
	}
    }
  UNBLOCK_INPUT;
}

/* Lower screen S.  */
void
x_lower_screen (s)
     struct screen *s;
{
  BLOCK_INPUT;
  if (s->visible)
    XLowerWindow (x_current_display, XtWindow(s->display.x->widget));
  UNBLOCK_INPUT;
}

/* Change from withdrawn state to mapped state. */
void
x_make_screen_visible (s)
     struct screen *s;
{
  BLOCK_INPUT;
  if (!s->visible)
    XMapRaised (x_current_display, XtWindow (s->display.x->widget));
  else
    x_raise_screen (s, 0);
  UNBLOCK_INPUT;
}

/* Change from mapped state to withdrawn state. */
void
x_make_screen_invisible (s)
     struct screen *s;
{
  if (! s->visible)
    return;

  BLOCK_INPUT;
  if (!XWithdrawWindow (x_current_display,
			XtWindow (s->display.x->widget),
			DefaultScreen (x_current_display)))
    {
      UNBLOCK_INPUT;
      error (GETTEXT ("Can't notify window manager of iconification."));
    }
  UNBLOCK_INPUT;
}

/* Change window state from mapped to iconified. */
void
x_iconify_screen (s)
     struct screen *s;
{
  BLOCK_INPUT;
  if (!XIconifyWindow (x_current_display,
		       XtWindow(s->display.x->widget),
		       DefaultScreen (x_current_display)))
    {
      UNBLOCK_INPUT;
      error (GETTEXT ("Can't notify window manager of iconification."));
    }
  UNBLOCK_INPUT;

  s->iconified = 1;
}

extern Time mouse_timestamp;
extern int any_screen_has_focus_p;
extern int focus_has_changed;

/* Sets the X focus to screen s.  Should only be called from
   select_screen.  Will only set focus if any screen was given the focus
   by the WM. */
void
x_focus_screen (struct screen *s)
{
  XWindowAttributes xwa;
  struct x_display* x;

  if (!SCREEN_IS_X (s))
    return;

  x = s->display.x;

  if (!XtWindow (x->widget))
    return;

  BLOCK_INPUT;
  /* Always set the Xt keyboard focus. */
#ifdef EXTERNAL_WIDGET
  if (x->external_window_p)
    ExternalShellSetFocus (x->widget);
  else
#endif /* EXTERNAL_WIDGET */

    /* This is necessary under Motif in order to make it possible to click in
       a buffer and move focus out of a dialog box or control panel and back
       into emacs-land. */
    lw_set_keyboard_focus (x->widget, x->edit_widget);

  /* Do the ICCCM focus change if we don't have focus already and
     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.
   */
  if ((!x->focus_p || focus_has_changed) && any_screen_has_focus_p)
    {
      focus_has_changed = 1;
      XGrabServer (XtDisplay (x->widget));
      if (XGetWindowAttributes (XtDisplay (x->widget), XtWindow (x->widget),
				&xwa))
	s->visible = xwa.map_state == IsViewable;
      
      if (s->visible)
	{
	  Window focus;
	  int revert_to;
	  XGetInputFocus (XtDisplay (x->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 (x->widget), XtWindow (x->widget),
			      RevertToParent, mouse_timestamp);
	      XFlush (XtDisplay (x->widget));
	    }
	}
      XUngrabServer (XtDisplay (x->widget));
      XFlush (XtDisplay (x->widget)); /* hey, I'd like to DEBUG this... */
    }
  UNBLOCK_INPUT;
}

#ifdef EXTERNAL_WIDGET
static int (*xdw_old_handler) (Display *, XErrorEvent *);

static int
xdw_error_handler (Display *display,
	XErrorEvent *event)
{
  if (event->error_code == BadWindow)
    return 0;
  return xdw_old_handler (display, event);
}
#endif /* EXTERNAL_WIDGET */

/* Destroy the X window of screen S.  */
void
x_destroy_window (s)
     struct screen *s;
{
  Widget w = s->display.x->widget;
#ifdef EXTERNAL_WIDGET
  Display *display = XtDisplay (w);
#endif /* EXTERNAL_WIDGET */

  if (s->display.x->top_level_screen_p)
    x_wm_maybe_move_wm_command (s);

  BLOCK_INPUT;
#ifdef EXTERNAL_WIDGET
  /* Now comes the crufty part. */

  /* make sure real errors get reported now. */
  XSync (display, 0);

  /* we need to trap BadWindow errors here because the screen's windows
     might already be destroyed (esp. if the screen occupied a client
     widget and the client died).  Don't rely on our general error handler
     because the window-widget correspondence is changed by the call to
     XtUnrealizeWidget(). */
  xdw_old_handler = XSetErrorHandler (xdw_error_handler);
  /* 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 (s->being_deleted)
    XtUnrealizeWidget (w);
  XtDestroyWidget (w);
  XSync (display, 0);
  XSetErrorHandler (xdw_old_handler);
#else
  XtDestroyWidget (w);
#endif /* EXTERNAL_WIDGET */
  UNBLOCK_INPUT;
  
  xfree (s->display.x);
}

#endif /* HAVE_X_WINDOWS */
