/* X Communication module for terminals which understand the X protocol.
   Copyright (C) 1988 Free Software Foundation, Inc.

/* On 4.3 this loses if it comes after x11term.h.
   On hp-ux it loses if it comes after config.h.  */
#include <signal.h>
#include <sys/ioctl.h>

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

#include "config.h"

#ifdef HAVE_X_WINDOWS

#include "lisp.h"
#undef NULL

/* Allow m- file to inhibit use of interrupt-driven input.  */
#ifdef BROKEN_FIONREAD
#undef FIONREAD
#endif

/* We are unable to use interrupts if FIONREAD is not available,
   so flush SIGIO so we won't try.  */
#ifndef FIONREAD
#ifdef SIGIO
#undef SIGIO
#endif
#endif

/* This may include sys/types.h, and that somehow loses
   if this is not done before the other system files.
   However, perhaps the problem has been avoided by loading types.h above.  */

#include "x11term.h"

#if defined(USG) && ! defined(IBMRTAIX) && !defined(OS_386ix)
#include <time.h>
#else
#include <sys/time.h>
#endif

#include <fcntl.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#ifdef BSD
#include <strings.h>
#endif
#include <sys/stat.h>

#include "dispextern.h"
#include "termhooks.h"
#include "termopts.h"
#include "termchar.h"

#include "sink11.h"
#include "sink11mask.h"

#ifndef NULL
#define NULL 0
#endif

#ifdef TRACE
#define fprintf trace2
#ifndef XDEBUG
#define XDEBUG
#endif
#endif

#ifdef POSIX_SIGNAL
extern sigset_t zero_sigmask;		/* sigmask with all 0's */
extern struct sigaction zero_sigaction;	/* sigaction with all 0's */

#ifndef signal
/* Rather than changing all of the signal calls directly, do so with
   the preprocessor.  This macro does not return anything.  */

#define signal(num, func) \
  { \
    struct sigaction sa; \
    sa = zero_sigaction; \
    sa.sa_handler = (void (*)()) func; \
    sigaction (num, &sa, (struct sigaction *)0); \
  }
#endif /* signal */
#endif /* POSIX_SIGNAL */

#include "screen.h"	/* [amc] */
#include "screenW.h"    /* [amc] */
#include "screenX.h"    /* [amc] */

#define min(a,b) ((a)<(b) ? (a) : (b))
#define max(a,b) ((a)>(b) ? (a) : (b))
        
extern int errno;
#ifdef POSIX_SIGNAL
#define sigblockx(sig) \
{ \
  sigset_t mask; \
  mask = zero_sigmask; \
  sigaddset (&mask, sig); \
  sigprocmask (SIG_BLOCK, &mask, (sigset_t *)0); \
}

#define sigunblockx(sig) \
{ \
  sigset_t mask; \
  mask = zero_sigmask; \
  sigaddset (&mask, sig); \
  sigprocmask (SIG_UNBLOCK, &mask, (sigset_t *)0); \
}
#else /* not POSIX_SIGNAL */
#define sigunblockx(sig) sigblock (0)
#define sigblockx(sig) sigblock (1 << ((sig) - 1))
#endif

#define METABIT 0200
#define MINWIDTH 12     /* In pixels */
#define MINHEIGHT 5     /* In pixels */
#define MAXHEIGHT 300   /* In lines */

char *progname;

XButtonEvent XXm_queue[XMOUSEBUFSIZE];
int XXm_queue_num, XXm_queue_in, XXm_queue_out;
extern Lisp_Object Vx_mouse_events;

/* default variables added by [amc] */

extern Display *XD_display;     /* default display */
extern char *XD_display_name;   /* its name */
extern char * XD_resource_name;
extern char * XD_screen_name;
/* end of changed section */

int configure_pending;
extern int initialized;

extern int screen_changed;

/* Function for init_keyboard to call with no args (if nonzero).  */
extern void (*keyboard_init_hook) ();

extern int xargc;
extern char **xargv;

int XXdebug;
int XXpid;

static char  *XXicon_name;      /* user-supplied icon info */
static int   XXicon_usebitmap;  /* Use bitmap or not */

#ifndef CB
static int flexlines;           /* last line affected by dellines or
                                 * inslines functions */
#endif

static int SavedX, SavedY;      /* Where the cursor was before update
                                 * started */


static int InUpdate;            /* many of functions here may be invoked
                                 * even if no update in progress; when
                                 * no update is in progress the action
                                 * can be slightly different */

Pixmap SinkPixmap, SinkMaskPixmap;

#ifndef CB
char *fore_color;       /* Variables to store color names */
char *back_color;
char *brdr_color;
char *curs_color;
char *mous_color;

static char  *temp_font;                /* needed because of loading hacks */
static char  *temp_reverseVideo;
static char  *temp_borderWidth;
static char  *temp_internalBorder;

#endif

int updated[MAXHEIGHT];
extern Atom XA_current;

/* 0 for no focus, 1 for Pointer focus, 2 for Explicit focus */
char X_focus;

int (*handler)();

static void x_init_1 ();

char *rindex();

/* useful macros */
#define CHARX(a,x)	((a)*(x)->font_width+(x)->in_border)
#define CHARY(a,x)	((a)*(x)->font_height+(x)->in_border)

#define HL(x)  ((x)->highlight ? (x)->gc_rev : (x)->gc_norm)

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* HLmode -- Changes the GX function for output strings.  Could be used to
 * change font.  Check an XText library function call.
 */
CHLmode (new, rb)
	int new;
	struct Root_Block *rb;
    {
    XXSCREEN(rb->x11)->highlight = new;
    }
HLmode (new) int new; { return CHLmode(new,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* External interface to control of standout mode.
   Call this when about to modify line at position VPOS
   and not change whether it is highlighted.  */

CXTreassert_line_highlight (highlight, vpos, rb)
	int highlight, vpos;
	struct Root_Block *rb;
    {
    CHLmode (highlight,rb);
    }
XTreassert_line_highlight (highlight, vpos)
	int highlight, vpos;
{ return CXTreassert_line_highlight(highlight,vpos,cur_root); }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Call this when about to modify line at position VPOS
   and change whether it is highlighted.  */

CXTchange_line_highlight (new_highlight, vpos, first_unused_hpos, rb)
	int new_highlight, vpos, first_unused_hpos;
	struct Root_Block *rb;
    {
    CHLmode (new_highlight,rb);
    CXTtopos (vpos, 0, rb);
    Cx_clear_end_of_line (0, rb);
    }

XTchange_line_highlight (new_highlight, vpos, first_unused_hpos)
	int new_highlight, vpos, first_unused_hpos;
    {
    return CXTchange_line_highlight(new_highlight,vpos,
				   first_unused_hpos,cur_root);
    }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Used for starting or restarting (after suspension) the X window.  Puts the
 * cursor in a known place, update does not begin with this routine but only
 * with a call to DoDsp.
 */

CXTset_terminal_modes (rb)
	struct Root_Block *rb;
    {
    int stuffpending;
    XS_DEF;
#ifdef XDEBUG
    fprintf (stderr, "XTset_terminal_modes\n");
#endif
    
    InUpdate = 0;
    stuffpending = 0;
    if (!initialized)
	{
	xs->cursor_exists = 0;
	xs->vis_x = 0;
	xs->vis_y = 0;
        }
    CXTclear_screen (rb);
    }
XTset_terminal_modes () { return CXTset_terminal_modes(cur_root); }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* XTtopos moves the cursor to the correct location and checks whether an
 * update is in progress in order to toggle it on.
 */

CXTtopos (row, col, rb)
     register int row, col;
	struct Root_Block *rb;
    {
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
#ifdef XDEBUG
    fprintf (stderr, "XTtopos (X %d, Y %d)\n",col,row);
#endif
    
    BLOCK_INPUT ();
    ws->cursor_x = col;  ws->cursor_y = row;
    
    if (InUpdate)
	{
	if (xs->cursor_exists) CCursorToggle (rb);
	UNBLOCK_INPUT ();
	return;
	/* Generally, XTtopos will be invoked */
	/* when InUpdate with !CursorExists */
	/* so that wasteful XFlush is not called */
	}
    if (row == xs->vis_y && col == xs->vis_x)
	{
	if (!xs->cursor_exists) CCursorToggle (rb);
	XFlush (xs->display);
	UNBLOCK_INPUT ();
	return;
	}
    /* if it's there now, take it away */
    if (xs->cursor_exists) CCursorToggle (rb);
    /* move it to the right location */
    xs->vis_x = col;
    xs->vis_y = row;
    /* put it back - this should always be true, I think */
    if (!xs->cursor_exists) CCursorToggle (rb);
    XFlush (xs->display);
    UNBLOCK_INPUT ();
    }
XTtopos(row, col) int row,col;
    { return CXTtopos(row,col,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Used to get the terminal back to a known state after resets.  Usually
 * used when restarting suspended or waiting emacs
 */

Ccleanup (rb)
	struct Root_Block *rb;
    {
    inverse_video = 0;
    CHLmode (0, rb);
    }
cleanup() { return Ccleanup(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Erase current line from current column to column END.
   Leave cursor at END.  */

CXTclear_end_of_line (end, rb)
	register int end;
	struct Root_Block *rb;
    {
    register int numcols;
    WS_DEF; XS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf (stderr, "XTclear_end_of_line (to %d)\n",end);
#endif
    
    if (ws->cursor_y < 0 || ws->cursor_y >= ws->height)
	return;
    
    if (end <= ws->cursor_x)
	return;
    if (end >= ws->width)
	end = ws->width;
    
    numcols = end - ws->cursor_x;
    BLOCK_INPUT ();
    if (ws->cursor_y == xs->vis_y
	&& xs->vis_x >= ws->cursor_x
	&& xs->vis_x < end
	&& xs->cursor_exists
	)
	CCursorToggle (rb);
    if (xs->highlight)
	XFillRectangle (xs->display, xs->xid, xs->gc_norm,
			CHARX(ws->cursor_x,xs),CHARY(ws->cursor_y,xs),
			xs->font_width*numcols,xs->font_height);
    else
	XClearArea (xs->display, xs->xid,
		    CHARX(ws->cursor_x,xs),CHARY(ws->cursor_y,xs),
		    xs->font_width*numcols,xs->font_height,0);
    CXTtopos (ws->cursor_y, end, rb);
    UNBLOCK_INPUT ();
    }
XTclear_end_of_line(end) int end;
    { return CXTclear_end_of_line(end,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Erase current line from column START to right margin.
   Leave cursor at START.  */

Cx_clear_end_of_line (start, rb)
     register int start;
	struct Root_Block *rb;
    {
    register int numcols;
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf (stderr, "x_clear_end_of_line (start %d)\n",first_blank);
#endif
    
    if (ws->cursor_y < 0 || ws->cursor_y >= ws->height)
	return;
    
    if (start >= ws->width)
	return;
    if (start < 0)
	start = 0;
    
    numcols = ws->width - start;
    BLOCK_INPUT ();
    if (ws->cursor_y == xs->vis_y && xs->vis_x >= start)
	if (xs->cursor_exists) CCursorToggle (rb);
    if (xs->highlight)
	XFillRectangle (xs->display, xs->xid, xs->gc_norm,
			CHARX(start,xs),CHARY(ws->cursor_y,xs),
			xs->font_width*numcols,xs->font_height);
    else
	XClearArea (xs->display, xs->xid,
		    CHARX(start,xs),CHARY(ws->cursor_y,xs),
		    xs->font_width*numcols,xs->font_height,0);
    CXTtopos (ws->cursor_y, start, rb);
    UNBLOCK_INPUT ();
    }
x_clear_end_of_line(start) int start;
    { return Cx_clear_end_of_line(start,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CXTreset_terminal_modes (rb)
	struct Root_Block *rb;
    {
#ifdef XDEBUG
    fprintf (stderr, "XTreset_terminal_modes\n");
#endif
    
    CXTclear_screen (rb);
    }
XTreset_terminal_modes() { return CXTreset_terminal_modes(cur_root); }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
CXTclear_screen (rb)
	struct Root_Block *rb;
    {
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf (stderr, "XTclear_screen\n");
#endif
    
    BLOCK_INPUT ();
    CHLmode (0, rb);
    xs->cursor_exists = 0;
    
    ws->cursor_x = ws->cursor_y = 0;
    SavedX = 0;
    SavedY = 0;
    xs->vis_x = 0;
    xs->vis_y = 0;
    XClearWindow(xs->display, xs->xid);
    CCursorToggle (rb);
    if (!InUpdate)
	XFlush (xs->display);
    UNBLOCK_INPUT ();
    }
XTclear_screen() { return CXTclear_screen(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* used by dumprectangle which is usually invoked upon Expose
 * events which come from bit blt's or moving an obscuring opaque window
 */
	
Cdumpchars (ActiveScreen, numcols, tempX, tempY, tempHL, rb)
	register struct display_line **ActiveScreen;
	register int numcols;
	register int tempX, tempY, tempHL;
	struct Root_Block *rb;
    {
    WS_DEF; XS_DEF;

    if (numcols <= 0)
	return;
    
    if (numcols-1+tempX > ws->width)
	numcols = ws->width-tempX+1;
    
    if (tempX < 0 || tempX >= ws->width
	|| tempY < 0 || tempY >= ws->height)
	return;

#ifdef BUTTON
    PlotTextLine(ActiveScreen[tempY+1],tempY,tempX,numcols,rb);
#else    
    XDrawImageString(xs->display, xs->xid,
		     tempHL ? xs->gc_rev : xs->gc_norm,
		     CHARX(tempX,xs),CHARY(tempY,xs)+xs->font_base,
		     &ActiveScreen[tempY+1]->body[tempX],
		     numcols);
#endif
    }
dumpchars (ActiveScreen, numcols, tempX, tempY, tempHL)
	register struct display_line **ActiveScreen;
	register int numcols;
	register int tempX, tempY, tempHL;
    { return Cdumpchars(ActiveScreen,numcols,tempX,tempY,tempHL,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* When a line has been changed this function is called.  Due to various
 * bits of braindamage on the parts of both X11 and Emacs, the new
 * version of the line is simply output if this function is invoked while
 * in UpDate.  Sometimes writechars can be invoked when not in update if
 * text is to be output at the end of the line.  In this case the whole
 * line is not output.  Simply the new text at the current cursor
 * position given by vis_x,Y.  The cursor is moved to the end of the
 * new text.
 */

Cupdateline (first, rb)
        int first;
	struct Root_Block *rb;
    {
    register int temp_length;
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf(stderr, "updateline\n");
#endif /* XDEBUG */
    
    BLOCK_INPUT ();
    if ((ws->cursor_y < 0) || (ws->cursor_y >= ws->height)
	|| updated[ws->cursor_y])
	{
	UNBLOCK_INPUT ();
	return;
	}
    if (!first)
	updated[ws->cursor_y] = 1;
    if (xs->cursor_exists)
	CCursorToggle (rb);
    if (ws->desired_line[ws->cursor_y+1])
	temp_length = ws->desired_line[ws->cursor_y + 1]->length-first;
    else
	temp_length = 0;
    if (temp_length > 0)
	{
#ifdef BUTTON
	PlotTextLine(ws->desired_line[ws->cursor_y+1],ws->cursor_y,first,
		     temp_length,rb);
#else    
	XDrawImageString (xs->display, xs->xid, HL(xs),
			  CHARX(first,xs),CHARY(ws->cursor_y,xs)+xs->font_base,
			  &(ws->desired_line[ws->cursor_y + 1]->body[first]),
			  temp_length);
#endif
	/* this looks wrong to me - it should be temp_length+first, or just
         * line->length. The only reason it doesn't fail is that first is
	 * always 0.
	 */
	if (temp_length < ws->width)
	    Cx_clear_end_of_line (temp_length,rb);
	CXTtopos (WS->cursor_y, temp_length,rb);
	}
    else
	{
	Cx_clear_end_of_line (0,rb);
	CXTtopos (ws->cursor_y, 0,rb);
	}
    UNBLOCK_INPUT ();
    }
updateline(first) int first; { return Cupdateline(first, cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Cwritechars (start, end, rb)
        register char *start, *end;
	struct Root_Block *rb;
    {
    register int temp_length;
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf(stderr, "writechars (cursX %d temp_len %d InUpd %d)\n",
	    cursX, end-start+1, InUpdate);
#endif /* XDEBUG */
    
    BLOCK_INPUT ();
    
    if ((ws->cursor_y < 0) || (ws->cursor_y >= ws->height))
	{
	UNBLOCK_INPUT ();
	return;
	}
    
    if (xs->cursor_exists)
	CCursorToggle (rb);
    
    
    if (InUpdate)
	{
	/* Not reached in epoch version, not fixed */
	XDrawImageString (xs->display, xs->xid, HL(xs),
			  CHARX(ws->cursor_x,xs),
			  CHARY(ws->cursor_y,xs)+xs->font_base,
			  start,end - start + 1);
	CXTtopos (ws->cursor_y, temp_length,rb);
	
	UNBLOCK_INPUT ();
	return;
	}
    
    if ((xs->vis_x < 0) || (xs->vis_x >= ws->width))
	{
	UNBLOCK_INPUT ();
	return;
	}
    if ((xs->vis_y < 0) || (xs->vis_y >= ws->height))
	{
	UNBLOCK_INPUT ();
	return;
	}
    if (((end - start) + xs->vis_x) >= ws->width)
	end = start + (ws->width - (xs->vis_x + 1));
    if (end >= start)
	{
	/* Not reached in epoch version, not fixed */
	XDrawImageString (xs->display, xs->xid, HL(xs),
			  CHARX(xs->vis_x,xs),
			  CHARY(xs->vis_y,xs)+xs->font_base,
			  start,((end - start) + 1));
	xs->vis_x = xs->vis_x + (end - start) + 1;
	}
    if (!xs->cursor_exists)
	CCursorToggle (rb);
    UNBLOCK_INPUT ();
    }
writechars(start,end) int start,end;
    { return Cwritechars(start,end,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CXTwrite_chars (start, len, rb)
     register char *start;
     register int len;
	struct Root_Block *rb;
    {
#ifdef XDEBUG
    fprintf (stderr, "XTwrite_chars (len %d)\n",len);
#endif

    Cwritechars (start, start+len-1, rb);
    }
static
XTwrite_chars (start, len) int start,len;
    { return CXTwrite_chars(start,len,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* [amc] */
CXTflash (rb)
	struct Root_Block *rb;
{
#ifdef HAVE_TIMEVAL
  struct timeval to;
#endif
  XGCValues gcv;
  GC thegc;
  XS_DEF; WS_DEF;
  BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
  fprintf (stderr, "XTflash\n");
#endif

  gcv.function = GXxor;
  gcv.foreground = xs->foreground ^ xs->background;
  gcv.fill_style = FillSolid;

  BLOCK_INPUT ();

  thegc = XCreateGC(xs->display, xs->xid,
		    GCFunction|GCForeground|GCFillStyle, &gcv);
  XFillRectangle (xs->display, xs->xid, thegc, 0, 0,
		  xs->pixwidth,xs->pixheight);
  /* changed to pixwidth/pixheight from
     SCREEN_WIDTH*XXfontw+2*cur_Xscreen->in_border,
     SCREEN_HEIGHT*XXfonth+2*cur_Xscreen->in_border);
     */
  XFlush (xs->display);
    
    UNBLOCK_INPUT ();
    
#if 0
    to.tv_sec = 0;
    to.tv_usec = 250000;
    
    select(0, 0, 0, 0, &to);
#endif

    BLOCK_INPUT ();
    
    XFillRectangle (xs->display, xs->xid, thegc, 0, 0,
		    xs->pixwidth,xs->pixheight);
    
    XFreeGC(xs->display, thegc);
    XFlush (xs->display);
    
    UNBLOCK_INPUT ();
    }
/* - - - */
XTflash()
{
  FlashScreen(cur_Xscreen->display, cur_Xscreen->xid,
	      cur_Xscreen->foreground, cur_Xscreen->background,
	      cur_Xscreen->pixwidth, cur_Xscreen->pixheight);
}
/*  { return CXTflash(cur_root); } */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CXTfeep (rb)
	struct Root_Block *rb;
   {
   XS_DEF;
   int volume = 50;
   BLOCK_INPUT_DECLARE ();

#ifdef CB
   {
     extern Lisp_Object Vepoch_bell_volume;
     if (XTYPE(Vepoch_bell_volume) == Lisp_Int)
       {
	 volume = XINT(Vepoch_bell_volume);
	 if (volume < -100 || volume > 100) volume = 50;
       }
   }
#endif

#ifdef XDEBUG
   fprintf (stderr, "XTfeep\n");
#endif
   BLOCK_INPUT ();
   XBell (xs->display,volume);
   UNBLOCK_INPUT ();
   }
XTfeep() { return CXTfeep(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Artificially creating a cursor is hard, the actual position on the
 * screen (either where it is or last was) is tracked with vis_x,Y.
 * Gnu Emacs code tends to assume a cursor exists in hardward at cursX,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.
 */

CCursorToggle(rb)
	struct Root_Block *rb;
    {
    register struct display_line **ActiveScreen;
    register GC cursorGC;
    int x,y;
    WS_DEF; XS_DEF;

    if (!xs->mapped)
	{
	xs->cursor_exists = 0;
	return 0;
	/* Currently the return values are not */
	/* used, but I could anticipate using */
	/* them in the future. */
        }
    
    if (xs->vis_x < 0 || xs->vis_x >= ws->width
	|| xs->vis_y < 0 || xs->vis_y >= ws->height)
	{
	/* Not much can be done */
	XFlush (xs->display);
	xs->cursor_exists = 0;
	return 0;
	}

    ActiveScreen = ws->phys_line;

    /* hack! [amc]
     * problem - there are cursors in both the minibuffer and the edit
     * screen. Solution - if we are toggling on other than the current
     * screen, force the cursor into non-existence. Since this is a toggle,
     * claiming that the cursor is already there will keep it off.
     */
    if (rb != cur_root) xs->cursor_exists = 1; /* DIE SCUM CURSOR FROM HELL! */
    x = xs->vis_x; y = xs->vis_y;

    if (ActiveScreen && ActiveScreen[y+1] && x < ActiveScreen[y+1]->length)
	{
	if (xs->cursor_exists)
#ifdef BUTTON
	  ShipOutTextBlock(ActiveScreen[y+1]->body+x,1,x,y,0,
			   ActiveScreen[y+1]->attrib[x],rb);
/*	    PlotTextLine(ActiveScreen[xs->vis_y+1],xs->vis_y,xs->vis_x,
			 1,rb); */
#else    
	    XDrawImageString(xs->display, xs->xid, xs->gc_norm,
			     CHARX(xs->vis_x,xs),
			     CHARY(xs->vis_y,xs)+xs->font_base,
			     &ActiveScreen[xs->vis_y+1]->body[xs->vis_x],
			     1);
#endif
	else if (! X_focus)
	    {
#ifdef BUTTON
	  ShipOutTextBlock(ActiveScreen[y+1]->body+x,1,x,y,0,
			   ActiveScreen[y+1]->attrib[x],rb);
/*	    PlotTextLine(ActiveScreen[xs->vis_y+1],xs->vis_y,xs->vis_x,
			 1,rb); */
#else    
	    XDrawImageString(xs->display, xs->xid, xs->gc_norm,
			     CHARX(xs->vis_x,xs),
			     CHARY(xs->vis_y,xs)+xs->font_base,
			     &ActiveScreen[xs->vis_y+1]->body[xs->vis_x],
			     1);
#endif
	    XDrawRectangle (xs->display, xs->xid, xs->gc_norm,
			    CHARX(xs->vis_x,xs),CHARY(xs->vis_y,xs),
			    xs->font_width - 1, xs->font_height - 1);
	    }
	else
#ifdef BUTTON
	    /* use the reverse attribute for plotting the cursor */
	  ShipOutTextBlock(ActiveScreen[y+1]->body+x,1,x,y,1,
			   ActiveScreen[y+1]->attrib[x],rb);
#else
	XDrawImageString(xs->display, xs->xid, xs->gc_curs,
			 CHARX(xs->vis_x,xs),
			 CHARY(xs->vis_y,xs)+xs->font_base,
			 &ActiveScreen[xs->vis_y+1]->body[xs->vis_x],
			 1);
#endif
	}
    else
	{
	if (xs->cursor_exists)
	    XClearArea (xs->display, xs->xid,
			CHARX(xs->vis_x,xs),
			CHARY(xs->vis_y,xs),
			xs->font_width, xs->font_height, 0);
	else if (! X_focus)
	    XDrawRectangle (xs->display, xs->xid, xs->gc_norm,
			    CHARX(xs->vis_x,xs),
			    CHARY(xs->vis_y,xs),
			    xs->font_width - 1, xs->font_height - 1);
	else
	    XDrawImageString(xs->display, xs->xid, xs->gc_curs,
			     CHARX(xs->vis_x,xs),
			     CHARY(xs->vis_y,xs)+xs->font_base,
			     " ", 1);
	}
    
    xs->cursor_exists = !xs->cursor_exists;
    
    if (!InUpdate)
	XFlush (xs->display);
    
    return 1;
    }
CursorToggle() { return CCursorToggle(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* 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. */

CClearCursor (rb)
	struct Root_Block *rb;
    {
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
    BLOCK_INPUT ();
    if (!xs->mapped)
	{
	xs->cursor_exists = 0;
	UNBLOCK_INPUT ();
	return;
        }
    
    if (xs->vis_x < 0 || xs->vis_x >= ws->width
	|| xs->vis_y < 0 || xs->vis_y >= ws->height)
	{
	/* Not much can be done */
	xs->cursor_exists = 0;
	UNBLOCK_INPUT ();
	return;
        }
    
    XClearArea (xs->display, xs->xid,
		CHARX(xs->vis_x,xs),CHARY(xs->vis_y,xs),
		xs->font_width, xs->font_height, 0);
    
    xs->cursor_exists = 0;
    UNBLOCK_INPUT ();
    }
static ClearCursor() { return CClearCursor(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
CXTupdate_begin (rb)
	struct Root_Block *rb;
    {
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    register int i;
    
#ifdef XDEBUG
    fprintf (stderr, "XTupdate_begin\n");
#endif
    
    BLOCK_INPUT ();
    InUpdate = 1;
    if (xs->cursor_exists)
	CCursorToggle(rb);
    
    for (i=0;i<MAXHEIGHT;i++)
	updated[i] = 0;
    
    SavedX = ws->cursor_x;
    SavedY = ws->cursor_y;
    /* Thw initial "hardware" cursor position is */
    /* saved because that is where gnu emacs */
    /* expects the cursor to be at the end of */
    /* the update */
    
    UNBLOCK_INPUT ();
    }
XTupdate_begin() { return CXTupdate_begin(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
CXTupdate_end (rb)
	struct Root_Block *rb;
    {
    XS_DEF;
    BLOCK_INPUT_DECLARE ();
    
#ifdef XDEBUG
    fprintf (stderr, "XTupdate_end\n");
#endif
    
    BLOCK_INPUT ();
    if (xs->cursor_exists)
	CCursorToggle(rb);
    
    InUpdate = 0;
    CXTtopos (SavedY, SavedX, rb); /* XTtopos invokes cursor toggle */
    XFlush (xs->display);
    UNBLOCK_INPUT ();
    }
XTupdate_end() { return CXTupdate_end(cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Used for Expose events.  Have to get the text
 * back into the newly blank areas.
 */

Cdumprectangle (top, left, rows, cols, rb)
	register int top, left, rows, cols;
	struct Root_Block *rb;
    {
    register struct display_line **ActiveScreen;
    register int ourindex;
    int localX, localY, localHL;
    XS_DEF; WS_DEF;
    
    if (top < 0)
	top = 0;
    if (left < 0)
	left = 0;
    rows += top;
    cols += left;
    top /= xs->font_height;
    /* Get row and col containing up and */
    /* left borders of exposed region -- */
    /* round down here*/
    left /= xs->font_width;
    rows += xs->font_height-1;
    cols += xs->font_width-1;
    rows /= xs->font_height;
    /* Get row and col containing bottom and */
    /* right borders -- round up here */
    rows -= top;
    cols /= xs->font_width;
    cols -= left;
    
    if (rows < 0)
	return;
    if (cols < 0)
	return;
    if (top > ws->height - 1)
	return;
    if (left > ws->width - 1)
	return;
    if (xs->vis_x >= left && xs->vis_x < left + cols
	&& xs->vis_y >= top && xs->vis_y < top + rows
	&& rb == cur_root)
	CClearCursor (rb);
    
    if (InUpdate && ws->desired_line)
	ActiveScreen = ws->desired_line;
    else
	if (ws->phys_line)
	    /* When queue is dumped in update this */
	    ActiveScreen = ws->phys_line;
        else
	    return;
    
    /* should perhaps be DesiredScreen */
    /* but PhysScreen is guaranteed to contain */
    /* data which was good for every line on */
    /* screen. For desired screen only for */
    /* lines which are changing.  Emacs does */
    /* not consider a line within a newly */
    /* exposed region necessarily to have */
    /* been changed.  Emacs knows nothing */
    /* about Expose events. */
    
    for (localY = top, ourindex = 0;
	 ourindex < rows && localY < ws->height;
	 ++ourindex, ++localY)
	{
	if (localY < 0 || localY >= ws->height
	    || !ActiveScreen[localY+1]
	    || left+1 > ActiveScreen[localY+1]->length)
	    continue;
	localX = left;
	localHL = ActiveScreen[localY+1]->highlighted;
	Cdumpchars (ActiveScreen,
		   min (cols,ActiveScreen[localY+1]->length-localX),
		   localX, localY, localHL, rb);
	}
    if (!InUpdate && rb == cur_root && !xs->cursor_exists)
	CCursorToggle (rb);
    /* Routine usually called */
    /* when not in update */
    }
dumprectangle (top, left, rows, cols)
	register int top, left, rows, cols;
    { return Cdumprectangle(top,left,rows,cols,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* What sections of the window will be modified from the UpdateDisplay
 * routine is totally under software control.  Any line with Y coordinate
 * greater than flexlines will not change during an update.  This is really
 * used only during dellines and inslines routines (scraplines and stufflines)
 */

CXTset_terminal_window (n, rb)
	register int n;
	struct Root_Block *rb;
    {
    WS_DEF;
#ifdef XDEBUG
    fprintf (stderr, "XTset_terminal_window\n");
#endif
    
    if (n <= 0 || n > ws->height)
	ws->flexlines = ws->height;
    else
	ws->flexlines = n;
    }
XTset_terminal_window(n) int n; { return CXTset_terminal_window(n,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
CXTins_del_lines (vpos, n, rb)
     int vpos, n;
	struct Root_Block *rb;
    {
#ifdef XDEBUG
    fprintf (stderr, "XTins_del_lines\n");
#endif
    
    CXTtopos (vpos, 0, rb);
    if (n >= 0)
	Cstufflines (n, rb);
    else
	Cscraplines (-n, rb);
    }
XTins_del_lines(vpos, n) int vpos,n;
    { return CXTins_del_lines(vpos,n,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CXTinsert_chars (start, len, rb)
	register char *start;
	register int len;
	struct Root_Block *rb;
    {
#ifdef XDEBUG
    fprintf (stderr, "XTinsert_chars\n");
#endif
    
    Cupdateline (0,rb);
    }
static XTinsert_chars(start,len) int start,len;
    { return CXTinsert_chars(start,len,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CXTdelete_chars (n, rb)	register int n;	struct Root_Block *rb;
{ Cupdateline (0, rb); }
static XTdelete_chars(n) int n; { return CXTdelete_chars(n,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Cstufflines (n, rb)
     register int n;
     struct Root_Block *rb;
{
  register int topregion, bottomregion;
  register int length, newtop;
  WS_DEF; XS_DEF;
  BLOCK_INPUT_DECLARE ();
    
  if (ws->cursor_y >= ws->flexlines)
    return;
    
  BLOCK_INPUT ();
    
  if (xs->cursor_exists) CCursorToggle(rb);
    
  topregion = ws->cursor_y;
  bottomregion = ws->flexlines-(n+1);
  newtop = ws->cursor_y+n;
  length = bottomregion-topregion+1;
    
  if (length > 0 && newtop <= ws->flexlines)
    {
      XCopyArea (xs->display, xs->xid, xs->xid, xs->gc_norm,
		 xs->in_border, CHARY(topregion,xs),
		 ws->width * xs->font_width,
		 length*xs->font_height,
		 xs->in_border,CHARY(newtop,xs));
    }	
  newtop = min (newtop, ws->flexlines-1);
  length = newtop-topregion;
  if (length > 0)
    XClearArea (xs->display, xs->xid,
		xs->in_border,CHARY(topregion,xs),
		ws->width * xs->font_width,
		n*xs->font_height, 0);
  UNBLOCK_INPUT ();
}
stufflines(n) int n; { return Cstufflines(n, cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Cscraplines (n, rb)
     register int n;
	struct Root_Block *rb;
    {
    XS_DEF; WS_DEF;
    BLOCK_INPUT_DECLARE ();
    
    if (ws->cursor_y >= ws->flexlines)
	return;
    
    BLOCK_INPUT ();

    if (xs->cursor_exists) CCursorToggle(rb);
    
    if (ws->cursor_y+n >= ws->flexlines)
	{
	if (ws->flexlines >= (ws->cursor_y + 1))
	    XClearArea (xs->display, xs->xid,
			xs->in_border,CHARY(WS->cursor_y,xs),
			ws->width * xs->font_width,
			(ws->flexlines-ws->cursor_y) * xs->font_height,
			0);
	UNBLOCK_INPUT ();
        }
    else
	{
	XCopyArea (xs->display, xs->xid, xs->xid, xs->gc_norm,
		   xs->in_border,CHARY(ws->cursor_y+n,xs),
		   ws->width * xs->font_width,
		   (ws->flexlines-ws->cursor_y-n)*xs->font_height,
		   xs->in_border,CHARY(ws->cursor_y,xs));
	
	XClearArea (xs->display, xs->xid,
		    xs->in_border,CHARY(ws->flexlines-n,xs),
		    ws->width * xs->font_width,
		    n*xs->font_height, 0);
	UNBLOCK_INPUT ();
        }
    }
scraplines(n) int n; { return Cscraplines(n, cur_root); }        

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void ShiftScreenLines(to,from,count,rb)
     int to;			/* target line number */
     int from;			/* source line number */
     int count;			/* number of lines to shift */
     struct Root_Block *rb;	/* the screen */
{
  int i;
  XS_DEF; WS_DEF;		/* access to internals */
  struct display_line **tmp;

  if (count == 1 || to == from) return;

  if (count < 0)
    {
      to = to + 1 + count;
      from = from + 1 + count;
      count = -count;
    }
  
  tmp = (struct display_line **) alloca(count * sizeof(struct display_line *));

  /* blit the display */
  XCopyArea(xs->display,xs->xid,xs->xid,xs->gc_norm,
	    0, CHARY(from-1,xs),
	    xs->pixwidth, count * xs->font_height,
	    0, CHARY(to-1,xs));

  /* Update the phys_line structure. This is a little tricky, so listen up!
   * Lines that have been discarded must be returned, and lines left behind
   * must be set to 0 (so that a particular display_line is not in phys_line
   * twice). Because the to / from regions may overlap, this can be a problem.
   * What we'll do (this is _so_ clever) is copy all of the from region out
   * of phys_line to a tmp array, zero the from region, and then copy back
   * from tmp to the to region, returning all non-zero lines. This will give
   * the correct results for the three areas, intersect, to-intersect,
   * from-intersect.
   */
  memcpy((VOID *)tmp, (VOID *) (ws->phys_line + from),
	 sizeof (struct display_line *) * count);
  memset( (VOID *) (ws->phys_line + from), 0,
	 sizeof (struct display_line *) * count);
  for ( i = 0 ; i < count ; ++i )
    {
      struct display_line *l = ws->phys_line[i+to];
      if (l) return_display_line(l);
      ws->phys_line[i+to] = tmp[i];
    }
}
  
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Substitutes for standard read routine.  Under X not interested in individual
 * bytes but rather individual packets.
 */

XTread_socket (sd, bufp, numchars)
	register int sd;
	register kbd_char_t *bufp;
	register int numchars;
    {
#ifdef XDEBUG
    fprintf(stderr,"XTread_socket\n");
#endif

    return (internal_socket_read (bufp, numchars));
    }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define MAPPINGBUFLEN 256 /* DENYS -- provide large buffer for long translations */

#ifdef DENYS_MOTION_COMPRESSION
static Display *motion_queued_display;
static Window   motion_queued_window;
static struct Root_Block *motion_queued_rb;
static struct X_Screen   *motion_queued_xs;
static int	motion_queued;
static int      motion_queued_row;
static int      motion_queued_col;
static int      motion_queued_valid;

static Display *motion_stashed_display;
static Window   motion_stashed_window;
static int      motion_stashed_x;
static int      motion_stashed_y;
static int      motion_stashed;

static void
x_stash_motion_event (event)
     XMotionEvent *event;
{
  motion_stashed_display = event->display;
  motion_stashed_window  = event->window;
  motion_stashed_x       = event->x;
  motion_stashed_y       = event->y;
  motion_stashed         = 1;
}

static void
x_queue_motion_event (display,window,x,y)
     Display *display;
     Window window;
     int x,y;
{
  XMotionEvent event;
  event.type = MotionNotify;
  event.display = display;
  event.window = window;
  event.x = x;
  event.y = y;
  x_queue_event(&event);
}

extern struct Root_Block *x_find_screen();

static void
x_motion_flush ()
{
  if (! motion_stashed) return;
  motion_stashed = 0;
  if (motion_queued_valid &&
      motion_queued_display == motion_stashed_display &&
      motion_queued_window  == motion_stashed_window)
    {
      int row = (motion_stashed_x - motion_queued_xs->in_border)/motion_queued_xs->font_height;
      int col = (motion_stashed_y - motion_queued_xs->in_border)/motion_queued_xs->font_width;
      if (row == motion_queued_row && col == motion_queued_col) return;
      motion_queued_row = row;
      motion_queued_col = col;
      x_queue_motion_event (motion_stashed_display,
			    motion_stashed_window,
			    motion_stashed_x,
			    motion_stashed_y);
      motion_queued_valid = 1;
      return;
    }
  if (motion_queued_display != motion_stashed_display ||
      motion_queued_window  != motion_stashed_window)
    {
      motion_queued_display = motion_stashed_display;
      motion_queued_window  = motion_stashed_window;
      motion_queued_rb      = x_find_screen (motion_stashed_window);
      if (! motion_queued_rb)
	{
	  motion_queued_valid = 0;
	  return;
	}
      motion_queued_xs      = XXSCREEN(motion_queued_rb->x11);
      motion_queued_row     = (motion_stashed_x - motion_queued_xs->in_border)/motion_queued_xs->font_height;
      motion_queued_col     = (motion_stashed_y - motion_queued_xs->in_border)/motion_queued_xs->font_width;
      x_queue_motion_event (motion_stashed_display,
			    motion_stashed_window,
			    motion_stashed_x,
			    motion_stashed_y);
      motion_queued_valid = 1;
    }
}

static void
x_motion_flush_using (display,window,x,y)
     Display * display;
     Window window;
     int x,y;
{
  if (! motion_stashed) return;
  if (display != motion_stashed_display || window != motion_stashed_window)
    {
      x_motion_flush ();
      motion_queued_valid = 0;
    }
  else
    {
      motion_stashed_x = x;
      motion_stashed_y = y;
      x_motion_flush ();
    }
}

static void
x_motion_flush_querying ()
{
  Window window;
  int    coord;
  if (! motion_stashed) return;
  XQueryPointer (motion_stashed_display,
		 motion_stashed_window,
		 &window,&window,
		 &coord,&coord,
		 &motion_stashed_x,
		 &motion_stashed_y,
		 &coord);
  x_motion_flush ();
}
#endif /* DENYS_MOTION_COMPRESSION */

/* instead of calling x_find_screen, let's cache the info and
 * avoid the call altogether. of course we must decide when to
 * invalidate the cached info. Let's not worry about this right
 * now. It seems fairly unlikely that the cached window id could
 * get recycled in between calls to this function. -- DENYS
 *
 * Moved out of the function - under SysV, static is a no-op, so they were
 * actually auto's (!), clearly bad news. Also, the cached rb could have been
 * deleted and GC'd between calls here, yet still have events coming in for
 * them, so cached_rb is cleared every time we come in here.
 */
static struct Root_Block *cached_rb;
static Window             cached_window;

#define X_FIND_SCREEN(w) \
  (cached_rb && (w) == cached_window) ? cached_rb : \
  ((cached_window = (w)),(cached_rb = x_find_screen(w)))

#define QUEUE_EVENT(e) \
  x_queue_event(e) , *bufp++ = EPOCH_EVENT_CHAR , ++count , --numchars
#ifdef X_COMPOSE
  static XComposeStatus status;
#endif
internal_socket_read(bufp, numchars)
        register kbd_char_t *bufp;
        register int numchars;
    {
    /* Number of keyboard chars we have produced so far.  */
    int count = 0;
    int nbytes,rows,cols;
    char mapping_buf[MAPPINGBUFLEN];
    BLOCK_INPUT_DECLARE ();
    XEvent event;
    KeySym keysym;
    struct Root_Block *rb,*x_find_screen();
    struct X_Screen *xs;
    struct W_Screen *ws;
    Display *xdisp = XXSCREEN(mini_root->x11)->display;
#ifdef DENYS_ALARM
    extern int timer_went_off;
#endif /* DENYS_ALARM */
#ifdef DENYS_MOTION_COMPRESSION
    int motion_event = 0;
#endif

    BLOCK_INPUT ();
    cached_rb = 0;		/* might have been deleted and GC'd */

#ifndef HAVE_SELECT
    if (! (fcntl (fileno (stdin), F_GETFL, 0) & O_NDELAY))
	{
	extern int read_alarm_should_throw;
	read_alarm_should_throw = 1;
	XPeekEvent (xdisp,&event);
	read_alarm_should_throw = 0;
	}
#endif
    while (XPending (xdisp))
	{
	XNextEvent (xdisp,&event);
#if 0
	event.type &= 0177;         /* Mask out XSendEvent indication */
#endif
	
	switch (event.type)
	    {
	    case MappingNotify:
		XRefreshKeyboardMapping(&event);
		break;
	    
	    case MapNotify:
		rb = X_FIND_SCREEN(event.xmap.window);
		if (rb) XXSCREEN(rb->x11)->mapped = 1;
		QUEUE_EVENT(&event);
		break;
	    
	    case UnmapNotify:
		rb = X_FIND_SCREEN(event.xmap.window);
		if (rb) XXSCREEN(rb->x11)->mapped = 0;
		QUEUE_EVENT(&event);
		break;
	    
	    case ConfigureNotify:
		rb = X_FIND_SCREEN(event.xconfigure.window);
		if (!rb) break;
		xs = XXSCREEN(rb->x11);
		QUEUE_EVENT(&event);
	    
		/* Uglification : Orders are to put configure notify events
		 * into the Q. If it's a synthetic, then just store it. Else,
		 * process and then store if it is significant.
		 */
		if (event.xany.send_event == True) break;

		/* If we haven't changed by a character, don't bother */
		if (abs(xs->pixheight - event.xconfigure.height)
		    < xs->font_height
		    && abs(xs->pixwidth - event.xconfigure.width)
		    < xs->font_width
		    )
		    break;
	    
		xs->configure_pending = 1;
	    
		rows = (event.xconfigure.height-2*xs->in_border)/
		    xs->font_height;
		cols = (event.xconfigure.width-2*xs->in_border)/
		    xs->font_width;
		/* these are not quite right, but we'll fudge 'em so that they
		 * are actually the size the window should be for an integral
		 * number of chars, so that the "change is not worth noticing"
		 * check above functions correctly
		 */
		xs->pixwidth = cols*xs->font_width+2*xs->in_border;
		xs->pixheight = rows*xs->font_height+2*xs->in_border;

		/*
		 * [cjl] bugfix -- make sure this screen gets redisplayed
		 */
		screen_changed++;
		
		break;
	    
	    case Expose:
		rb = X_FIND_SCREEN(event.xexpose.window);
		if (!rb) break;
		xs = XXSCREEN(rb->x11);
		if (xs->configure_pending)
		    {
		    int width, height;
		    if (event.xexpose.count) break;

		    width = (xs->pixwidth-2*xs->in_border) / xs->font_width;
		    height = (xs->pixheight-2*xs->in_border) / xs->font_height;

		    /* remove this check - assume WM deals with it */
		    change_screen_size (height, width, rb);
		    
		    Cdumprectangle (0,0,xs->pixheight,xs->pixwidth, rb);

		    xs->configure_pending = 0;
		    break;
		    }
		Cdumprectangle (event.xexpose.y-xs->in_border,
				event.xexpose.x-xs->in_border,
				event.xexpose.height,
				event.xexpose.width, rb);
		break;
	    
	    case GraphicsExpose:
		rb = X_FIND_SCREEN(event.xexpose.window);
		if (!rb) break;
		xs = XXSCREEN(rb->x11);
		Cdumprectangle (event.xgraphicsexpose.y-xs->in_border,
			   event.xgraphicsexpose.x-xs->in_border,
			   event.xgraphicsexpose.height,
			   event.xgraphicsexpose.width, rb);
		break;
	    
	    case NoExpose:
		break;

	    /* Still not quite right. Here's the current scheme :
	     * xcrossing.focus -> Is this the window with focus or an inferior?
	     * X_focus : 0 for no-focus, 1 for pointer focus,
	     *           2 for explicit focus.
	     * Rules : Explicit focus is lost only through FocusOut, and set
	     * through FocusIn. Enter/Leave can set pointer focus iff
	     * no explicit focus. Enter/Leave are meaningless unless the
	     * focus field is set.
	     */

#ifdef DENYS_MOTION_COMPRESSION
	    case EnterNotify:
	    case FocusIn:
		/* this should never happen, but you never know */
		if (motion_event) { x_motion_flush (); motion_event = 0; }
		if (event.type == EnterNotify &&
		    (! event.xcrossing.focus || X_focus == 2)) break;
		X_focus = (event.type == FocusIn) ? 2 : 1;
		QUEUE_EVENT(&event);
		if (cur_Xscreen->cursor_exists)
		    {
		    CClearCursor(cur_root);
		    CCursorToggle(cur_root);
		    }
		break;

	    case LeaveNotify:
		if (motion_event)
		  {
		    x_motion_flush_using (event.xcrossing.display,
					  event.xcrossing.window,
					  event.xcrossing.x,
					  event.xcrossing.y);
		    motion_event = 0;
		  }
		if (! event.xcrossing.focus || X_focus == 2) break;
	    case FocusOut:
		X_focus = 0;
		QUEUE_EVENT(&event);
		if (cur_Xscreen->cursor_exists)
		    {
		    CClearCursor(cur_root);
		    CCursorToggle(cur_root);
		    }
		break;
#else /* not DENYS_MOTION_COMPRESSION */
	    case EnterNotify :
	    case LeaveNotify :
		if (! event.xcrossing.focus || X_focus == 2) break;
	    case FocusIn :
	    case FocusOut :
		X_focus =
		    event.type == FocusIn ? 2 :
			(event.type == EnterNotify ? 1 : 0);
		QUEUE_EVENT(&event);
		if (cur_Xscreen->cursor_exists)
		    {
		    CClearCursor(cur_root);
		    CCursorToggle(cur_root);
		    }
		break;
#endif /* not DENYS_MOTION_COMPRESSION */

	    case PropertyNotify : QUEUE_EVENT(&event); break;
	    case MotionNotify :
#ifdef DENYS_MOTION_COMPRESSION
	        x_stash_motion_event (&event);
		motion_event = 1;
		break;
#else /* not DENYS_MOTION_COMPRESSION */
		/* discard stacked up motion events */
		while (XCheckTypedWindowEvent(XD_display,event.xany.window,
					      MotionNotify,&event))
		    ;
		/* Q the last one only */
		QUEUE_EVENT(&event);
		break;
#endif /* not DENYS_MOTION_COMPRESSION */

	    /* currently the only client message expected is internal, used
	     * to trip the SIGIO handler if there are waiting unprocessed
	     * X Events. This is indicated by a message type of XA_current.
	     */
	    case ClientMessage :
		if (event.xclient.message_type != XA_current)
		    QUEUE_EVENT(&event);
		break;

	    case KeyPress:
#ifdef DENYS_MOTION_COMPRESSION
		if (motion_event)
		  {
		    x_motion_flush_using (event.xkey.display,
					  event.xkey.window,
					  event.xkey.x,
					  event.xkey.y);
		    motion_event = 0;
		  }
#endif /* DENYS_MOTION_COMPRESSION */
		/* When/If Compose status is supported, change the 0
		 * to &status
		 */
#ifndef X_COMPOSE		
		nbytes = XLookupString (&event,
					mapping_buf, MAPPINGBUFLEN, &keysym,
					0);
#else
		nbytes = XLookupString (&event,
					mapping_buf, MAPPINGBUFLEN, &keysym,
					&status);
#endif		
		/* Someday this will be unnecessary as we will
		   be able to use XRebindKeysym so XLookupString
		   will have already given us the string we want. */
		/* [amc] Added XRebindKeysym, so if nbytes is set, then the
		 * user rebound the key, so don't do any special processing
		 * on
		 */
		if (nbytes == 0)
		  {
		    extern Lisp_Object Vepoch_function_key_mapping;
		    extern char * stringFuncVal();

		    if (!EQ(Qnil,Vepoch_function_key_mapping) &&
			(IsFunctionKey(keysym) || IsMiscFunctionKey(keysym)))
			{
			strcpy(mapping_buf,"\033[");
			strcat(mapping_buf,stringFuncVal(keysym));
#ifdef sun
			strcat(mapping_buf,"z");
#else
			strcat(mapping_buf,"~");
#endif /* sun */
			nbytes = strlen(mapping_buf);
			}
		    else
		      {
			nbytes = 1;
			mapping_buf[1] = 0; /* effect of strcpy() */
			switch (keysym)
			  {
			  case XK_Left: *mapping_buf = 2; break;
			  case XK_Right: *mapping_buf = 6; break;
			  case XK_Up: *mapping_buf = 020; break;
			  case XK_Down: *mapping_buf = 016; break;
			  default : nbytes = 0; break;
			  }
		      }
		  }
		if (nbytes)
		    {
		    if ((event.xkey.state & Mod1Mask) && nbytes == 1)
			*mapping_buf |= METABIT;
		    if (numchars > nbytes)
			{
			count += nbytes;
			numchars -= nbytes;
#ifdef CB
			{
			  int i;
			  for ( i = 0 ; i < nbytes ; ++i)
			    *bufp++ = mapping_buf[i];
			}
#else
			bcopy (mapping_buf, bufp, nbytes);
			bufp += nbytes;
#endif
			}
		    }
		break;
	    
	    case ButtonPress:
	    case ButtonRelease:
#ifdef DENYS_MOTION_COMPRESSION
		if (motion_event)
		  {
		    x_motion_flush_using (event.xbutton.display,
					  event.xbutton.window,
					  event.xbutton.x,
					  event.xbutton.y);
		    motion_event = 0;
		  }
#endif /* DENYS_MOTION_COMPRESSION */
		if (!EQ(Qnil,Vx_mouse_events))
		    {
		    QUEUE_EVENT(&event);
		    break;
		    }
		*bufp++ = (char) 'X' & 037;
		++count;
		--numchars;
		*bufp++ = (char) '@' & 037;
		++count;
		--numchars;
		if (XXm_queue_num == XMOUSEBUFSIZE)
		    break;
		memcpy((char *) &(XXm_queue[XXm_queue_in]),
		       (char *) &(event.xbutton), sizeof(XButtonEvent));
		XXm_queue_num++;
		XXm_queue_in = (XXm_queue_in + 1) % XMOUSEBUFSIZE;
		break;
	    }
	}
    
/*    if (cur_Xscreen->cursor_exists)
	Cxfixscreen (cur_root);
*/

#ifdef DENYS_ALARM
    if (timer_went_off)		/* DENYS -- handle timer by faking an event */
      {
	timer_went_off = 0;
	event.xclient.type = LASTEvent;
	event.xclient.display = xdisp;
	event.xclient.window = XXSCREEN(mini_root->x11)->xid;
	QUEUE_EVENT(&event);
      }
#endif /* DENYS_ALARM */

#ifdef DENYS_MOTION_COMPRESSION
    if (motion_event) x_motion_flush_querying ();
#endif

    UNBLOCK_INPUT ();
    return count;
    }

/* Exit gracefully from gnuemacs, doing an autosave and giving a status.
 */

XExitGracefully ()
    {
    XCleanUp();
    exit (70);
    }

XIgnoreError ()
    {
    return 0;
    }

/* Here's the deal: under most systems, if IO errors are ignored, Epoch can
 * spin. However, under HPUX, this apparently doesn't happen, and normal
 * operations seems to cause errors (i.e., the error handler is called during
 * most IO operations, even though no errors have apparently occurred (!)).
 */
#ifdef HPUX
#define EPOCH_IOERRORHANDLER		XIgnoreError
#else
#define EPOCH_IOERRORHANDLER		XExitGracefully
#endif
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Cxfixscreen (rb)
	struct Root_Block *rb;
    {
    XS_DEF;
    BLOCK_INPUT_DECLARE ();
    
    /* Yes, this is really what I mean -- Check to see if we've
     * lost our connection */
    
    BLOCK_INPUT ();
    XSetErrorHandler(0);
    XSetIOErrorHandler(0);
    XNoOp (xs->display);
    XFlush (xs->display);
    XSetErrorHandler(handler);
    XSetIOErrorHandler(EPOCH_IOERRORHANDLER);
    if (!InUpdate && !xs->cursor_exists)
	CCursorToggle (rb);
    
    UNBLOCK_INPUT ();
    }
xfixscreen() { return Cxfixscreen(cur_root); }        

/* ------------------------------------------------------------
 */
static int  reversevideo;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* entry point for setting up X-windows.
 * Modified by amc
 */
x_term_init ()
    {
    register char *vardisplay;
    register int xxargc;
    register char **xxargv;
    char *ptr;
    XColor cdef;
    
    extern char *getenv ();
    extern XTinterrupt_signal ();
    extern char *malloc ();
    extern Lisp_Object Vxterm, Vxterm1, Qt;
    extern int XIgnoreError();
    int  ix;
#ifdef DENYS_ALARM
    extern void x_init_timer();
#endif
    
    /* set EMACS global flags to reflect using X-windows */
    baud_rate = 9600;
    min_padding_speed = 10000;
    must_write_spaces = 1;
    MetaFlag = 1;
    visible_bell = 1;
    inverse_video = 0;
    configure_pending = 0;
    
    fix_screen_hook = xfixscreen;
    clear_screen_hook = XTclear_screen;
    clear_end_of_line_hook = XTclear_end_of_line;
    ins_del_lines_hook = XTins_del_lines;
    change_line_highlight_hook = XTchange_line_highlight;
    insert_chars_hook = XTinsert_chars;
    write_chars_hook = XTwrite_chars;
    delete_chars_hook = XTdelete_chars;
    ring_bell_hook = XTfeep;
    reset_terminal_modes_hook = XTreset_terminal_modes;
    set_terminal_modes_hook = XTset_terminal_modes;
    update_begin_hook = XTupdate_begin;
    update_end_hook = XTupdate_end;
    set_terminal_window_hook = XTset_terminal_window;
    read_socket_hook = XTread_socket;
    topos_hook = XTtopos;
    reassert_line_highlight_hook = XTreassert_line_highlight;
    scroll_region_ok = 1;       /* we'll scroll partial screens */
    char_ins_del_ok = 0;
    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 */
    dont_calculate_costs = 1;
    
    reversevideo = 0;
    
    XXdebug = 0;
    XXm_queue_num = 0;
    XXm_queue_in = 0;
    XXm_queue_out = 0;
    
    handler = XIgnoreError;
    XSetErrorHandler (handler);
    XSetIOErrorHandler (EPOCH_IOERRORHANDLER);
    
    XXicon_usebitmap = 0;

#ifdef NO_COED    
    temp_font = "fixed";
#endif
    XXpid = getpid ();
   
    signal (SIGPIPE, XExitGracefully);
    
#ifndef CANNOT_DUMP
    if (initialized)
#endif /* CANNOT_DUMP */
        Vxterm = Qt;
    
    Fset (intern ("window-system-version"), make_number (11));
    
    x_init_display ();
#ifdef DENYS_ALARM
    x_init_timer ();
#endif
    
    keyboard_init_hook = x_init_1;
    }

/* Initialize for keyboard input using X.
   This is called by init_keyboard via keyboard_init_hook.  */

static void
x_init_1 ()
    {
    Display *d = XXSCREEN(mini_root->x11)->display; /* see below */
#ifdef F_SETOWN
    extern int old_fcntl_owner;
#endif
/* [amc]
 * This is ugly, but I don't have time to figure out what to do about it.
 * The concept that there is only 1 input source is deeply imbedded in the
 * IO handling code. To handle multiple displays, that would all have to
 * be fixed, and something much more clever done here. For now, we will
 * restrict things to a single display, and therefore whatever display we
 * use is ok, since they are all the same.
 */
    dup2 (ConnectionNumber(d), 0);
    close (ConnectionNumber(d));
    ConnectionNumber(d) = 0;        /* Looks a little strange?
                                                 * check the def of the macro;
                                                 * it is a genuine lvalue */
    setpgrp (0,getpid());
        
#ifdef F_SETOWN
    old_fcntl_owner = fcntl (0, F_GETOWN, 0);
    fcntl (0, F_SETOWN, getpid ());
#endif

    /* Enable interrupt_input because otherwise we cannot asynchronously
       detect C-g sent as a keystroke event from the X server.  */
    Fset_input_mode (Qt, Qnil);
    }

XSetFlash ()
    {
    ring_bell_hook = XTflash;
    }

XSetFeep ()
    {
    ring_bell_hook = XTfeep;
    }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
 * Interpreting incoming keycodes. Should have table modifiable as needed
 * from elisp.
 */

#ifdef sun
char *stringFuncVal(keycode)
        KeySym keycode;
    {
    switch (keycode)
	{
	case XK_L1: return("192");
	case XK_L2: return("193");
	case XK_L3: return("194");
	case XK_L4: return("195");
	case XK_L5: return("196");
	case XK_L6: return("197");
	case XK_L7: return("198");
	case XK_L8: return("199");
	case XK_L9: return("200");
	case XK_L10: return("201");
	
	case XK_R1: return("208");
	case XK_R2: return("209");
	case XK_R3: return("210");
	case XK_R4: return("211");
	case XK_R5: return("212");
	case XK_R6: return("213");
	case XK_R7: return("214");
	case XK_R8: return("215");
	case XK_R9: return("216");
	case XK_R10: return("217");
	case XK_R11: return("218");
	case XK_R12: return("219");
	case XK_R13: return("220");
	case XK_R14: return("221");
	case XK_R15: return("222");
	
	case XK_Break: return("223");		/* sun3 "Alternate" key */
	
	case XK_F1: return("224");
	case XK_F2: return("225");
	case XK_F3: return("226");
	case XK_F4: return("227");
	case XK_F5: return("228");
	case XK_F6: return("229");
	case XK_F7: return("230");
	case XK_F8: return("231");
	case XK_F9: return("232");
	
	default: return("-1");
        }
    }
#else
char *stringFuncVal(keycode)
        KeySym keycode;
    {
    switch (keycode)
	{
        case XK_F1: return("11");
        case XK_F2: return("12");
        case XK_F3: return("13");
        case XK_F4: return("14");
        case XK_F5: return("15");
        case XK_F6: return("17");
        case XK_F7: return("18");
        case XK_F8: return("19");
        case XK_F9: return("20");
        case XK_F10: return("21");
        case XK_F11: return("23");
        case XK_F12: return("24");
        case XK_F13: return("25");
        case XK_F14: return("26");
        case XK_F15: return("28");
        case XK_Help: return("28");
        case XK_F16: return("29");
        case XK_Menu: return("29");
        case XK_F17: return("31");
        case XK_F18: return("32");
        case XK_F19: return("33");
        case XK_F20: return("34");
        
        case XK_Find : return("1");
        case XK_Insert: return("2");
        case XK_Delete: return("3");
        case XK_Select: return("4");
        case XK_Prior: return("5");
        case XK_Next: return("6");

        default: return("-1");
        }
    }
#endif /* not sun */
        
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/* ------------------------------------------------------------
 *  Load a font by name.  Return the font pointer, or NULL if
 *  it can't be loaded.  Do all appropriate calculations.
 * Modified by AMC
 */
#ifndef CB
XFontStruct * XT_CalcForFont(fontname)
        char  *fontname;
    {
    XFontStruct  *fontp;
    
    if ( (fontp = XLoadQueryFont(XD_display, fontname)) == (XFontStruct *) 0 )
        {
        return  (XFontStruct *) NULL;
        }
    
    XD_font_id = fontp->fid;
    XD_gcval.font = XD_font_id;
    XD_font_height = fontp->ascent + fontp->descent;
    XD_font_width = fontp->max_bounds.width;
    XD_font_base = fontp->ascent;
    
    return  fontp;
    }

#endif /* not defined CB */
#endif /* HAVE_X_WINDOWS */

/*#include "xundebug.h"*/
