/*--------------------------------*-C-*---------------------------------*
 * File:	screen.c
 *
 * This module is all new by Robert Nation
 * <nation@rocket.sanders.lockheed.com>
 *
 * Extensive modifications by mj olesen <olesen@me.QueensU.CA>
 * No additional restrictions are applied.
 *
 * As usual, the author accepts no responsibility for anything, nor does
 * he guarantee anything whatsoever.
 *
 * Design of this module was heavily influenced by the original xvt
 * design of this module. See info relating to the original xvt elsewhere
 * in this package.
 *----------------------------------------------------------------------*/
#include "rxvt.h"

#include <ctype.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include "command.h"
#include "debug.h"
#include "graphics.h"
#include "xsetup.h"
#include "screen.h"

#define PROP_SIZE	4096
#define	TABSIZE		8	/* default tab size */

/* Memory copy methods -- rgg 20/11/95 */
#define MEMCOPY         memcpy          /* no overlap -- rgg 04/11/95 */
/* #define MEMCOPY      memmove */      /* allows overlap -- rgg 04/11/95 */

/*----------------------------------------------------------------------*
 * extern functions referenced
 */
/*----------------------------------------------------------------------*
 * extern variables referenced
 */
/*----------------------------------------------------------------------*
 * extern variables declared here
 */
#ifndef NO_BOLDCOLOR
unsigned int color_fgbg = SET_RSTYLE (RS_None, COLORFG, COLORBG);
#endif
#ifndef NO_CURSORCOLOR
unsigned int color_cursor = 0;
#endif

/* convert pixel dimensions to row/column values */
#define Pixel2Col(x)	(((x) - TermWin_internalBorder) / TermWin.fwidth)
#define Pixel2Row(y)	(((y) - TermWin_internalBorder) / TermWin.fheight)

#define Col2Pixel(col)	((col) * TermWin.fwidth  + TermWin_internalBorder)
#define Row2Pixel(row)	((row) * TermWin.fheight + TermWin_internalBorder)

/* #define DEBUG_SCREEN */
/* to do self-check for internal screen corruption */

/* #define DEBUG_COLORS */
/* to print out current color/renditions as they change */

/* #define DEBUG_REFRESH */
/* to print out color/renditions as they change */

/*----------------------------------------------------------------------*
 * local variables
 */
typedef unsigned char text_t;
typedef	unsigned int rend_t;
static rend_t rstyle = SET_RSTYLE (RS_None, COLORFG, COLORBG);

#define SPACE_CHAR ' '
#define CURRENT_RSTYLE	(rstyle)

void ZERO_LINES (text_t *, rend_t *, int nlines);
void BLANK_FILL (text_t *, rend_t *, int nchars);

/* zero both text and rendition */
#define ZERO_LINES(ptext,prend,nlines)	do {\
int cnt = (nlines) * (TermWin.ncol+1);\
if (cnt > 0) {\
memset ((ptext), 0, (cnt * sizeof(text_t)));\
memset ((prend), 0, (cnt * sizeof(rend_t)));\
}} while (0)

/* fill text with spaces and rendition = (rstyle) */
#define	BLANK_FILL(ptext,prend,nchars)	do {\
rend_t *pr = prend; int cnt = (nchars);\
if (cnt > 0) {\
memset ((ptext), ' ', (cnt * sizeof(text_t)));\
while (cnt-- > 0) *pr++ = rstyle;\
}} while (0)

/*
 * how the screen accounting works
 *
 * `text' contains text including the scrollback buffer. Each line is a
 * fixed length [TermWin.ncol+1] with the final character of each:
 *	'\n':	for wrapped lines
 *	`\0':	for non-wrapped lines
 *
 * `rend' contains rendition information (font, bold, color, etc)
 *
 * the layout:
 * Rows [0 .. (TermWin.saveLines-1)] == scrollback region
 * Rows [TermWin.saveLines .. TermWin.saveLines + (TermWin.nrow-1)] ==
 *	screen region [0 .. (TermWin.nrow-1)]
 *
 * `row', `tscroll', `bscroll' are bounded by (0, TermWin.nrow)
 *
 * `col' is bounded by (0, TermWin.ncol)
 *
 * `TermWin.saveLines'
 *	is the maximum number of lines to save in the scrollback buffer.
 *	This is a fixed number for any particular rxvt instance and is set
 *	by the option `-sl' or resource `saveLines'
 *
 * `TermWin.hist_count'
 *	how many lines have been scrolled (saved)
 *		0 <= TermWin.hist_count <= TermWin.saveLines
 *
 * `TermWin.hist_start'
 *	the offset back into the scrollback buffer for our current view
 *		-(TermWin.hist_count) <= TermWin.hist_start <= 0
 *
 * The selection region is defined for [0 .. (TermWin.nrow-1)], which
 * corresponds to the regular screen and for [-1 .. -(TermWin.hist_count)]
 * which corresponds to the scrolled region.
 *
 * Actually, the current accounting mechanism for selection is really
 * screwed-up.  Although it is apparently permissible to select from the
 * scrollback buffer, all of the row values for the selection mechanism are
 * in the [0 .. TermWin.nrow-1] range which gives some very strange results
 * and is the likely cause of all the selection problems. -- but I think
 * these selection problems are fixed as of v2.15
 */

typedef struct {
   text_t *text;		/* all the text, including scrollback	*/
   rend_t *rend;		/* rendition, using the `RS_' flags	*/
   int row, col;		/* cursor position			*/
   int tscroll, bscroll;	/* top/bottom of scroll region		*/
   int relative;		/* relative origin mode flag		*/
   int cursor_visible;		/* cursor visible?			*/
   int autowrap;		/* auto-wrap flag			*/
   int wrapnext;		/* need to wrap for next char?		*/
   int insert;			/* insert mode (vs. overstrike)		*/
   int charset;			/* character set number [0..3]		*/
} screen_t;

static screen_t screen = { NULL, NULL, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 };

#ifdef NO_SECONDARY_SCREEN
# define NSCREENS	0
#else
# define NSCREENS	1
static screen_t swap_screen = { NULL, NULL, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 };
#endif
static int current_screen = PRIMARY;

static int rvideo = 0;

static char *tabs = NULL;	/* a 1 for a location with a tab-stop */
static text_t *linebuf = NULL;

#ifdef KANJI
static int multiByte = 0;
#endif

/* Data for save-screen */
static struct {
   int row, col, charset;
   char charset_char;
   rend_t rstyle;
} save = { 0, 0, 0, 'B', SET_RSTYLE (RS_None, COLORFG, COLORBG) };

/* This tells what's actually on the screen */
static text_t *drawn_text = NULL;
static rend_t *drawn_rend = NULL;

static char charsets [4] = {'B','B','B','B'};	/* all ascii */

/* save selection text with '\n' line endings, but translate
 * '\n' to '\r' for pasting */
static struct {
   unsigned char *text;		/* selected text */
   int len;			/* length of selected text */
   int op;			/* current operation */
#define SELECTION_CLEAR	0
#define SELECTION_BEGIN	1
#define SELECTION_INIT	2
#define SELECTION_CONT	3
#define SELECTION_DONE	4
   int screen;			/* which screen is being used */
   struct {
      int row, col;
   } beg, end, mark;
} selection = {NULL, 0, SELECTION_CLEAR, PRIMARY, {0, 0}, {0, 0}, {0, 0} };
/* also could add in these:
 * int firstr, lastr;		-- firstr <= row < lastr
 * if trying to implement X11 mouse highlighting
 */

/*----------------------------------------------------------------------*
 * local functions referenced
 */
static int scroll_text (int row1, int row2, int count);
static inline void selection_check (void);

/*----------------------------------------------------------------------*/
/*
 * simple check for integrity of the screen data structures
 */
#ifdef DEBUG_SCREEN
static void
check_text (const char *str)
{
   int r, x;
   static const char *prev = "?";

   fprintf (stderr, "%s\n", str);
   for (r = 0, x = TermWin.ncol;
	r < (TermWin.nrow + TermWin.saveLines);
	r++, x += (TermWin.ncol+1))
     {
	text_t final = screen.text [x];
     	if (final != '\0' && final != '\n')
	  {
	     fprintf (stderr,"%s: %s Violation on row %d\n", str, prev, x);
	     exit (EXIT_FAILURE);
	  }
     }
   MEM_CHECK ("check_text", str);
   prev = str;
}
#else
# define check_text(a)	((void)0)
#endif	/* DEBUG_SCREEN */

/*
 * Kanji translation units
 */
#ifdef KANJI
static void
eucj2jis (unsigned char *str, int len)
{
   register int i;
   for (i = 0; i < len; i++)
     str [i] &= 0x7F;
}

static void
sjis2jis (unsigned char *str, int len)
{
   register int i;
   for (i = 0; i < len; i += 2, str += 2)
     {
	unsigned char *high, *low;
	high = str;
	low  = str + 1;
	(*high) -= (*high > 0x9F ? 0xB1: 0x71);
	*high = (*high) * 2 + 1;
	if (*low > 0x9E)
	  {
	     *low -= 0x7E;
	     (*high)++;
	  }
	else
	  {
	     if (*low > 0x7E)
	       (*low)--;
	     *low -= 0x1F;
	  }
     }
}
static void (*kanji_decode) (unsigned char *str, int len) = eucj2jis;

void
set_kanji_encoding (const char *str)
{
   if (str == NULL || !*str)
     return;

   if (!strcmp (str, "sjis"))		kanji_decode = sjis2jis;
   else if (!strcmp (str, "eucj"))	kanji_decode = eucj2jis;
}
#endif	/* KANJI */

/*
 * add COUNT blank lines in the current rendition style
 */
static inline void
blank_lines (text_t *text, rend_t *rend, int count)
{
   int r;

   if (count <= 0)
     return;

   /* fill with blank lines */
   BLANK_FILL (text, rend, (count * (TermWin.ncol+1)));

   /* terminate each line */
   text += (TermWin.ncol);
   for (r = 0; r < count; r++, text += (TermWin.ncol+1))
     {
	*text = '\0';
     }
}

/*
 * Reset the screen - called whenever the screen needs to be repaired due
 * to re-sizing or initialization
 */
void
scr_reset (void)
{
   static int prev_nrow = -1, prev_ncol = -1;
   int i, ncol = 0;

   TermWin.hist_start = 0;

   if (prev_ncol == TermWin.ncol && prev_nrow == TermWin.nrow) return;
#ifdef DEBUG_SCREEN
   fprintf (stderr, "reset\n");
#endif
   /* In case rows/columns are invalid */
   if (TermWin.ncol <= 0) TermWin.ncol = 80;
   if (TermWin.nrow <= 0) TermWin.nrow = 24;

#ifndef min
# define min(a,b) (((a)<(b)) ? (a) : (b))
# define max(a,b) (((a)>(b)) ? (a) : (b))
#endif
   ncol = min (prev_ncol, TermWin.ncol);

   if (prev_nrow < 0)
     TermWin.hist_count = 0;

   for (i = 0; i <= NSCREENS; i++)
     {
	screen_t *scr;
	text_t *old_text;
	rend_t *old_rend;
	int r, oldr, oldr_max, histsize;

	/* allocate space for scrollback (primary screen) + screen */
#if NSCREENS
	if (i)
	  {
	     histsize = 0;
	     scr = &swap_screen;
	  }
	else
#endif
	  {
	     histsize = TermWin.saveLines;
	     scr = &screen;
	  }
 	/* copy from old buffer to new buffer, as appropriate. */
	oldr = oldr_max = prev_nrow;
	if (prev_nrow > 0)
	  {
	     int n = (TermWin.nrow - prev_nrow);
	     if (n > 0)		/* window made taller */
	       {
		  oldr = (TermWin.nrow);
		  if (histsize)
		    {
		       if (n <= TermWin.hist_count)   /* enough */
			 {
			    scr->row += (n);
			    oldr = oldr_max;
			 }
		       else
			 {
			    scr->row += (TermWin.hist_count);
			    oldr     -= (TermWin.hist_count);
			 }
		       TermWin.hist_count -= (n);
		    }
	       }
	     else if (n < 0)	/* window made shorter */
	       {
		  if (scr->row < TermWin.nrow)
		    {
		       oldr_max = TermWin.nrow;
		    }
		  else		/* put extra into scrolled */
		    {
		       oldr_max = (scr->row+1);
		       if (histsize)
			 TermWin.hist_count += (oldr_max - TermWin.nrow);
		    }
		  oldr = oldr_max;
	       }
	     oldr_max += histsize;
	     oldr += histsize;
	     oldr--;
	  }

	if (scr->row < 0) scr->row = 0;
	else if (scr->row >= TermWin.nrow) scr->row = (TermWin.nrow-1);

	if (scr->col < 0) scr->col = 0;
	else if (scr->col >= TermWin.ncol) scr->col = (TermWin.ncol-1);

	/* reset scroll regions */
	scr->tscroll = 0; scr->bscroll = (TermWin.nrow-1);

	old_text = scr->text;
	old_rend = scr->rend;

	scr->text = MALLOC (((histsize+TermWin.nrow)*(TermWin.ncol+1)) * sizeof(text_t),
			    "text");
	scr->rend = MALLOC (((histsize+TermWin.nrow)*(TermWin.ncol+1)) * sizeof(rend_t),
			    "rend");

	blank_lines (scr->text, scr->rend, (histsize+TermWin.nrow));
	if (ncol > 0)
	  {
	     for (r = (TermWin.nrow+histsize-1);
		  r >= 0 && oldr >= 0;
		  r--, oldr--)
	       {
		  if (oldr < oldr_max)
		    {
		       int roffset = r * (TermWin.ncol+1);
		       int oroffset = oldr * (prev_ncol+1);
		       memcpy (&scr->text [roffset], &old_text [oroffset],
			       ncol * sizeof(text_t));
		       memcpy (&scr->rend [roffset], &old_rend [oroffset],
			       ncol * sizeof(rend_t));
		    }
	       }
	  }
	FREE (old_text, "scr_reset", "scr_reset");
	FREE (old_rend, "scr_reset", "scr_reset");
     }

   /* Make sure the cursor is on the screen */
   if (TermWin.hist_count < 0)
     TermWin.hist_count = 0;
   else if (TermWin.hist_count > TermWin.saveLines)
     TermWin.hist_count = TermWin.saveLines;

   prev_ncol = TermWin.ncol;
   prev_nrow = TermWin.nrow;

   drawn_text = REALLOC (drawn_text,
			 (TermWin.nrow * (TermWin.ncol+1)) * sizeof(text_t),
			 "drawn_text");
   drawn_rend = REALLOC (drawn_rend,
			 (TermWin.nrow * (TermWin.ncol+1)) * sizeof(rend_t),
			 "drawn_rend");

   ZERO_LINES (drawn_text, drawn_rend, TermWin.nrow);

   /* ensure the cursor is on the screen */
   if (save.row >= TermWin.nrow) save.row = (TermWin.nrow-1);
   if (save.col >= TermWin.ncol) save.col = (TermWin.ncol-1);

   tabs = REALLOC (tabs, TermWin.ncol * sizeof(char), "tabs");
   linebuf = REALLOC (linebuf, (TermWin.ncol+1) * sizeof(text_t), "linebuf");

   memset (tabs, 0, TermWin.ncol * sizeof(char));
#if TABSIZE
   for (i = 0; i < TermWin.ncol; i += TABSIZE)
     tabs [i] = 1;
#endif
   tty_resize ();
}

/*
 * Restore power-on configuration
 * Clears screen, restores default fonts, etc
 *
 * also reset the scrollback buffer
 */
void
scr_poweron (void)
{
   screen_t *scr;
   int i;

   TermWin.hist_start = 0;
   TermWin.hist_count = 0;	/* xterm doesn't do this */

   memset (charsets, 'B', sizeof(charsets));
   rvideo = 0;
   scr_rendition (0, ~RS_None);

#if NSCREENS
   scr_change_screen (SECONDARY);
   scr_erase_screen (2);
#endif
   scr_change_screen (PRIMARY);
   scr_erase_screen (2);

   for (i = 0, scr = &screen; i <= NSCREENS; i++)
     {
	scr->tscroll = 0;
	scr->bscroll = (TermWin.nrow-1);
	scr->row = scr->col = 0;
	scr->relative = 0;
	scr->autowrap = 1;
	scr->insert = 0;
	scr->cursor_visible = 1;
	scr->charset = 0;
#if NSCREENS
	scr = &swap_screen;
#endif
     }
   scr_cursor (SAVE);
   ZERO_LINES (drawn_text, drawn_rend, TermWin.nrow);

   scr_reset ();
   XClearWindow (Xdisplay, TermWin.vt);
   scr_refresh (SLOW_REFRESH);
   Gr_reset ();
}

/*
 * Set the rstyle parameter to reflect the selected font
 */
static inline void
set_font_style (void)
{
   check_text ("font style");

   rstyle &= ~RS_fontMask;
   switch (charsets [screen.charset]) {
      /* DEC Special Character and Line Drawing Set */
    case '0': rstyle |= RS_acsFont;	break;
      /* United Kingdom (UK) */
    case 'A': rstyle |= RS_ukFont;	break;
      /* United States (USASCII) */
      /* case 'B': break; */		/* ascii */

      /* <: Multinational character set */
      /* 5: Finnish character set */
      /* C: Finnish character set */
      /* K: German character set */
   }
}

/*
 * Save/restore the cursor position and rendition style
 */
void
scr_cursor (int mode)
{
#ifdef DEBUG_COLORS
   debug_colors ();
#endif
   switch (mode) {
    case SAVE:
      save.row = screen.row;
      save.col = screen.col;
      save.rstyle = CURRENT_RSTYLE;
      save.charset = screen.charset;
      save.charset_char = charsets [screen.charset];
      break;
    case RESTORE:
      screen.row = save.row;
      screen.col = save.col;
      rstyle = save.rstyle;
      screen.charset = save.charset;
      charsets [screen.charset] = save.charset_char;
      set_font_style ();
      break;
   }
#ifdef DEBUG_COLORS
   debug_colors ();
#endif
}

void
scr_cursor_visible (int mode)
{
   screen.cursor_visible = mode;
}

/*
 * Handle a backspace
 */
void
scr_backspace (void)
{
   check_text ("backspace");

   if (selection.op) selection_check ();
   if (screen.col == 0 && screen.row > 0)
     {
	screen.row--;
	screen.col = (TermWin.ncol-1);
     }
   else if (screen.wrapnext)
     {
	int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);
	screen.text [roffset + (TermWin.ncol)] = '\0';
	screen.wrapnext = 0;
     }
   else
     scr_gotorc (0, -1, RELATIVE);
}

/*
 * Change between the main and alternate screen
 */
int
scr_change_screen (int scrn)
{
   int x;
   TermWin.hist_start = 0;

   if (current_screen == scrn)
     return current_screen;
   else
     {
	int tmp;
	tmp = current_screen;
	current_screen = scrn;
	scrn = tmp;
     }

#if NSCREENS
   check_text ("change screen");

   /* swap screens, but leave scrollback untouched */
     {
	const int count = (TermWin.nrow) * (TermWin.ncol+1);
	const int roffset = (TermWin.saveLines) * (TermWin.ncol+1);

	text_t * text = &screen.text [roffset];
	rend_t * rend = &screen.rend [roffset];

	for (x = 0; x < count; x++)
	  {
	     text_t t;
	     rend_t r;

	     t = text [x];
	     text [x] = swap_screen.text [x];
	     swap_screen.text [x] = t;

	     r = rend [x];
	     rend [x] = swap_screen.rend [x];
	     swap_screen.rend [x] = r;
	  }
     }

   x = screen.row;
   screen.row = swap_screen.row;
   swap_screen.row = x;

   x = screen.col;
   screen.col = swap_screen.col;
   swap_screen.col = x;

   x = screen.relative;
   screen.relative = swap_screen.relative;
   swap_screen.relative = x;

   x = screen.autowrap;
   screen.autowrap = swap_screen.autowrap;
   swap_screen.autowrap = x;

   x = screen.wrapnext;
   screen.wrapnext = swap_screen.wrapnext;
   swap_screen.wrapnext = x;

   x = screen.insert;
   screen.insert = swap_screen.insert;
   swap_screen.insert = x;

   x = screen.charset;
   screen.charset = swap_screen.charset;
   swap_screen.charset = x;

   screen.cursor_visible = 1;
   swap_screen.cursor_visible = 1;

   if (graphics_up)
     {
	Gr_scroll (0);
	Gr_ChangeScreen ();
     }
#else
   /* put contents of secondary screen into the scrollback */
   if (graphics_up)
     Gr_ClearScreen ();
   else if (current_screen == PRIMARY)
     scroll_text (0, (TermWin.nrow-1), TermWin.nrow);

#endif
   return scrn;
}

#ifdef RXVT_GRAPHICS
/* return the current foreground/background colors */
int scr_get_fgcolor (void) { return GET_FGCOLOR (rstyle); }
int scr_get_bgcolor (void) { return GET_BGCOLOR (rstyle); }
#endif

#ifdef DEBUG_COLORS
static void
debug_colors (void)
{
   int color;
   char * name [] = { "foreground", "background",
	"black", "red", "green", "yellow",
	"blue", "magenta", "cyan", "white"};

   fprintf (stderr, "Color ( ");
   if (rstyle & RS_RVid)	fprintf (stderr, "rvideo ");
   if (rstyle & RS_Bold)	fprintf (stderr, "bold ");
   if (rstyle & RS_Blink)	fprintf (stderr, "blink ");
   fprintf (stderr, "): ");

   color = GET_FGCOLOR (rstyle);
#ifndef NO_BOLDCOLOR
   if (color >= BOLDMIN && color <= BOLDMAX)
     {
	color -= (BOLDMIN - COLORMIN);
	fprintf (stderr, "bright ");
     }
#endif
   fprintf (stderr, "%s on ", name [color]);

   color = GET_BGCOLOR (rstyle);
#ifndef NO_BOLDCOLOR
   if (color >= BOLDMIN && color <= BOLDMAX)
     {
	color -= (BOLDMIN - COLORMIN);
	fprintf (stderr, "bright ");
     }
#endif
   fprintf (stderr, "%s\n", name [color]);
}
#endif	/* DEBUG_COLORS */

/*
 * Set foreground/background text color
 */
void
scr_color (int color, int Intensity)
{
   switch (color) {
    case RESTORE_FG: color = COLORFG; break;
    case RESTORE_BG: color = COLORBG; break;

    default:
      if (Xdepth > 2)
	{
#ifndef NO_BOLDCOLOR
	   if ((rstyle & Intensity) && color >= COLORMIN && color <= COLORMAX)
	     color += (BOLDMIN - COLORMIN);
	   else if (color >= BOLDMIN && color <= BOLDMAX)
	     return;		/* already bold enough! */
#endif
	   /*nil*/;
	}
      else
	{
	   /* normal/reverse */
	   color -= COLORMIN;
	   switch (Intensity) {
	    case RS_Bold:  color = (color != 7 ? COLORFG : COLORBG); break;
	    case RS_Blink: color = (color == 0 ? COLORFG : COLORBG); break;
	   }
	}
      break;
   }

   switch (Intensity) {
    case RS_Bold:  rstyle = SET_FGCOLOR (rstyle, color); break;
    case RS_Blink: rstyle = SET_BGCOLOR (rstyle, color); break;
   }
#ifdef DEBUG_COLORS
   debug_colors ();
#endif
}

/*
 * Change the rendition style
 */
void
scr_rendition (int set, int style)
{
   int color;

#ifdef DEBUG_COLORS
   debug_colors ();
#endif
   if (set)
     {
	rstyle |= style;	/* set rendition */
	switch (style) {
#ifndef NO_BOLDCOLOR
	 case RS_Bold:
	   color = GET_FGCOLOR (rstyle);
	   if (color == COLORFG) color = GET_FGCOLOR (color_fgbg);
	   scr_color (color, RS_Bold);
	   break;

	 case RS_Blink:
	   color = GET_BGCOLOR (rstyle);
	   if (color == COLORBG) color = GET_BGCOLOR (color_fgbg);
	   scr_color (color, RS_Blink);
	   break;
#endif	/* NO_BOLDCOLOR */

	 case RS_RVid:
	   if (rvideo)		/* faked reverse video mode */
	     rstyle &= ~(RS_RVid);
	   break;
	}
     }
   else
     {
	rstyle &= ~(style);	/* unset rendition */

	switch (style) {
	 case ~RS_None:		/* default fg/bg colors */
	   rstyle = SET_RSTYLE (RS_None, COLORFG, COLORBG);
	   if (rvideo)		/* faked reverse video mode */
	     rstyle |= RS_RVid;
	   break;

#ifndef NO_BOLDCOLOR
	 case RS_Bold:
	   color = GET_FGCOLOR (rstyle);
	   if (color >= BOLDMIN && color <= BOLDMAX)
	     {
		color -= (BOLDMIN - COLORMIN);
		scr_color (color, RS_Bold);
		if ((rstyle & RS_fgMask) == (color_fgbg & RS_fgMask))
		  scr_color (RESTORE_FG, RS_Bold);
	     }
	   break;

	 case RS_Blink:
	   color = GET_BGCOLOR (rstyle);
	   if (color >= BOLDMIN && color <= BOLDMAX)
	     {
		color -= (BOLDMIN - COLORMIN);
		scr_color (color, RS_Blink);
		if ((rstyle & RS_bgMask) == (color_fgbg & RS_bgMask))
		  scr_color (RESTORE_BG, RS_Blink);
	     }
	   break;
#endif	/* NO_BOLDCOLOR */

	 case RS_RVid:
	   if (rvideo)		/* faked reverse video mode */
	     rstyle |= RS_RVid;
	   break;
	}
     }
   /* set_font_style (); */
#ifdef DEBUG_COLORS
   debug_colors ();
#endif
}

void
scr_add_lines (const unsigned char * str, int nlines, int cnt)
{
   int i, roffset;		/* row offset */
#ifdef KANJI
   enum {SBYTE, WBYTE1, WBYTE2} chstat = SBYTE;
#endif

   TermWin.hist_start = 0;

   check_text ("add lines");
   if (selection.op) selection_check ();

   /* do scrolling up front, most of the time */
   if (nlines > 0)
     {
	nlines += (screen.row - screen.bscroll);

	if ((nlines > 0) &&
	    (screen.tscroll == 0) &&
	    (screen.bscroll == (TermWin.nrow-1)))
	  {
	     scroll_text (screen.tscroll, screen.bscroll, nlines);

	     /* add text into the scrollback buffer */
	     screen.row -= nlines;
	     if (screen.row < -TermWin.saveLines)
	       screen.row = -TermWin.saveLines;
	  }
     }

   if (screen.col >= TermWin.ncol) screen.col = (TermWin.ncol-1);

   roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);
   for (i = 0; i < cnt; i++)
     {
	/*
	 * Adds a single character at the current cursor location
	 * new lines in the string
	 */
	if (graphics_up) Gr_scroll (0);

#ifdef KANJI
	switch (chstat)
#endif
	  {
#ifdef KANJI
	   case WBYTE1:	break;		/* never happens? */

	   case WBYTE2:
	     rstyle |= RS_kanjiMask;
	     chstat = SBYTE;
	     break;

	   case SBYTE:
	     if (multiByte || (str [i] & 0x80))
	       {
		  rstyle &= ~RS_kanjiMask;
		  rstyle |= RS_kanji1;
		  chstat = WBYTE2;
	       }
	     else
#endif	/* KANJI */
	       switch (str [i]) {
		case 127:			continue; break;
		case '\t':	scr_tab (1);	continue; break;
		case '\n':
#ifdef AMIGA
		  screen.col = 0;
#endif
		  screen.wrapnext = 0;
		  screen.text [roffset + (TermWin.ncol)] = '\0';
		  if (screen.row == screen.bscroll)
		    scroll_text (screen.tscroll, screen.bscroll, 1);
		  else if (screen.row < (TermWin.nrow-1))
		    {
		       screen.row++;
		       roffset = ((screen.row + TermWin.saveLines) *
				  (TermWin.ncol+1));
		    }
		  continue; break;

		case '\r':
		  screen.col = 0;
		  screen.wrapnext = 0;
		  screen.text [roffset + (TermWin.ncol)] = '\0';
		  continue; break;

		default:
#ifdef KANJI
		  rstyle &= ~(RS_kanjiMask);
#endif
		  break;
	       }
	  }

	if (screen.wrapnext)
	  {
	     screen.text [roffset + (TermWin.ncol)] = '\n'; /* wrap line */
	     if (screen.row == screen.bscroll)
	       scroll_text (screen.tscroll, screen.bscroll, 1);
	     else if (screen.row < (TermWin.nrow-1))
	       {
		  screen.row++;
		  roffset = ((screen.row + TermWin.saveLines) *
			     (TermWin.ncol+1));
	       }
	     screen.col = 0;
	     screen.wrapnext = 0;
	  }
	if (screen.insert)
	  scr_insdel_chars (1, INSERT);
	screen.text [roffset + (screen.col)] = str [i];
	screen.rend [roffset + (screen.col)] = rstyle;

	screen.col++;
	if (screen.col == TermWin.ncol)
	  {
	     screen.col--;
	     screen.wrapnext = screen.autowrap;
	  }
     }
}

/*
 * Scroll the text on the screen
 * Scroll COUNT lines from ROW1 to ROW2 inclusive (ROW1 <= ROW2)
 * scrolling is up for a +ve COUNT and down for a -ve COUNT
 */
static int
scroll_text (int row1, int row2, int count)
{
   int r;
   text_t *t_dst, *t_src;
   rend_t *r_dst, *r_src;

   if (selection.op)		/* move selected region too */
     {
	selection.beg.row -= count;
	selection.end.row -= count;
	selection.mark.row -= count;
	/*
	 * could check ranges here and make sure selection is okay
	 * don't scroll into scrollback depending on the region etc,
	 * but leave for now
	 */
     }

   if (count > 0)		/* scroll up */
     {
	int n, x;
	/* if the line scrolls off the top of the screen,
	 * shift the entire scrollback buffer too */
	if ((row1 == 0) && (current_screen == PRIMARY))
	  {
	     row1 = -TermWin.saveLines;
	     TermWin.hist_count += count;
	     if (TermWin.hist_count > TermWin.saveLines)
	       TermWin.hist_count = TermWin.saveLines;
	  }

	x = ((row1 + TermWin.saveLines) * (TermWin.ncol+1));
	t_dst = &screen.text [x];
	r_dst = &screen.rend [x];

	n = (row2 - row1 + 1);
	if (count > n)
	  {
	     count = n;
	     n = 0;
	  }
	else
	  {
	     n -= count;
	  }

	x += count * (TermWin.ncol+1);
	t_src = &screen.text [x];
	r_src = &screen.rend [x];

	/* Forward overlapping memcpy's -- probably OK */
	if (n > 0)
	  {
	     n *= (TermWin.ncol+1);
	     MEMCOPY (t_dst, t_src, n * sizeof(text_t));
	     MEMCOPY (r_dst, r_src, n * sizeof(rend_t));
	     t_dst += n;
	     r_dst += n;
	  }
	*(t_dst-1) = '\0';	/* terminate previous line */

	/* copy blank lines in at the bottom */
	blank_lines (t_dst, r_dst, count);
     }
   else if (count < 0)		/* scroll down */
     {
	int x;

	/* do one line at a time to avoid backward overlapping memcpy's */
	x = (row2 + TermWin.saveLines) * (TermWin.ncol+1);
	t_dst = &screen.text [x];
	r_dst = &screen.rend [x];

	x += (count) * (TermWin.ncol+1);
	t_src = &screen.text [x];
	r_src = &screen.rend [x];
	for (r = row2; r >= (row1 - count); r--)
	  {
	     MEMCOPY (t_dst, t_src, (TermWin.ncol+1) * sizeof(text_t));
	     t_dst -= (TermWin.ncol+1);
	     t_src -= (TermWin.ncol+1);

	     MEMCOPY (r_dst, r_src, (TermWin.ncol+1) * sizeof(rend_t));
	     r_dst -= (TermWin.ncol+1);
	     r_src -= (TermWin.ncol+1);
	  }

	/* copy blank lines in at the top */
	for (; r >= row1; r--)
	  {
	     BLANK_FILL (t_dst, r_dst, TermWin.ncol);
	     t_dst [TermWin.ncol] = '\0';
	     t_dst -= (TermWin.ncol+1);
	     r_dst -= (TermWin.ncol+1);
	  }
     }
   if (graphics_up)
     Gr_scroll (count);
   return count;
}

/*
 * Move the cursor to a new tab position
 *
 * COUNT is +ve, move forward.  COUNT is -ve, move backward
 */
void
scr_tab (int count)
{
   int x = screen.col;

   if (count > 0) 		/* tab forward */
     {
	int i;
	for (i = x+1; i < TermWin.ncol; i++)
	  {
	     if (tabs [i])
	       {
		  x = i;
		  count--;
		  if (!count) break;
	       }
	  }
     }
   else if (count < 0)		/* tab backward */
     {
	int i;
	count = -count;
	for (i = x-1; i >= 0; i--)
	  {
	     if (tabs [i])
	       {
		  x = i;
		  count--;
		  if (!count) break;
	       }
	  }
     }
   else
     return;

   if (x != screen.col)
     scr_gotorc (0, x, R_RELATIVE);
}

/*
 * Move the cursor to a new position.
 * The relative argument is a pair of flags that specify relative
 * rather than absolute motion.
 */
void
scr_gotorc (int row, int col, int relative)
{
   TermWin.hist_start = 0;
   if (graphics_up) Gr_scroll (0);

   screen.col = (relative & C_RELATIVE ? (screen.col + col) : col);

   if (screen.col < 0) screen.col = 0;
   else if (screen.col >= TermWin.ncol) screen.col = (TermWin.ncol-1);

   if (screen.wrapnext)
     {
	int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);
	screen.text [roffset + (TermWin.ncol)] = '\0';
	screen.wrapnext = 0;
     }

   if (relative & R_RELATIVE)
     {
	if (row > 0)
	  {
	     if ((screen.row <= screen.bscroll) &&
		 (screen.row + row > screen.bscroll))
	       screen.row = screen.bscroll;
	     else
	       screen.row += row;
	  }
	else if (row < 0)
	  {
	     if ((screen.row >= screen.tscroll) &&
		 (screen.row + row < screen.tscroll))
	       screen.row = screen.tscroll;
	     else
	       screen.row += row;
	  }
     }
   else
     {
	if (screen.relative)		/* relative origin mode */
	  {
	     screen.row = row + screen.tscroll;
	     if (screen.row > screen.bscroll)
	       screen.row = screen.bscroll;
	  }
	else
	  screen.row = row;
     }
   if (screen.row < 0) screen.row = 0;
   else if (screen.row >= TermWin.nrow) screen.row = (TermWin.nrow-1);
}

/*
 * Move the cursor down one line and scroll if necessary
 */
void
scr_index (int dirn)
{
   TermWin.hist_start = 0;
   if (graphics_up) Gr_scroll (0);

   check_text ("index");

   if (screen.wrapnext)
     {
	int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);
	screen.text [roffset + (TermWin.ncol)] = '\0';
	screen.wrapnext = 0;
     }

   if ((screen.row == screen.bscroll && dirn == UP) ||
       (screen.row == screen.tscroll && dirn == DOWN))
     scroll_text (screen.tscroll, screen.bscroll, dirn);
   else
     screen.row += dirn;
   if (selection.op) selection_check ();
}

/*
 * erase part or the whole of a line
 */
void
scr_erase_line (int mode)
{
   int roffset, count;

   TermWin.hist_start = 0;
   if (graphics_up) Gr_scroll (0);

   roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);

   if (screen.wrapnext)
     {
	screen.text [roffset + (TermWin.ncol)] = '\0';
	screen.wrapnext = 0;
     }
   switch (mode) {
    case 0:		/* erase to end */
      check_text ("erase line End");
      roffset += (screen.col);
      count = (TermWin.ncol - screen.col);
      break;

    case 1:		/* erase to beginning */
      check_text ("erase line Start");
      count = (screen.col + 1);
      break;

    case 2:		/* erase entire */
      check_text ("erase line Entire");
      count = (TermWin.ncol);
      break;

    default:
      check_text ("erase line None");
      return;
   }

   BLANK_FILL (&screen.text [roffset], &screen.rend [roffset], count);

   check_text ("erase line Done");
   if (selection.op) selection_check ();
}

/*
 * erase part or the whole of the screen
 */
void
scr_erase_screen (int mode)
{
   int roffset, count;
   TermWin.hist_start = 0;

   roffset = (TermWin.saveLines) * (TermWin.ncol+1);
   switch (mode) {
    case 0:			/* erase to end */
      check_text ("erase screen End");
      scr_erase_line (0);
      count = (TermWin.nrow - (screen.row + 1));
      roffset += ((screen.row + 1) * (TermWin.ncol+1));
      break;

    case 1:			/* erase to beginning */
      check_text ("erase screen Start");
      scr_erase_line (1);
      count = screen.row;
      break;

    case 2:			/* erase entire */
      check_text ("erase screen Entire");
      Gr_ClearScreen ();
      count = TermWin.nrow;
      break;

    default:
      check_text ("erase screen None");
      return;
      break;
   }

   blank_lines (&screen.text [roffset], &screen.rend [roffset], count);
   check_text ("erase screen Done");
}

/*
 * Fill screen with E's
 */
void
scr_E (void)
{
   int r, roffset, count;

   TermWin.hist_start = 0;

   check_text ("E");

   roffset = (TermWin.saveLines) * (TermWin.ncol+1);

   count = (TermWin.nrow) * (TermWin.ncol+1);
   memset (&screen.text [roffset], 'E', count * sizeof(text_t));
   memset (&screen.rend [roffset], RS_None, count * sizeof(rend_t));

   for (r = 0; r < (TermWin.nrow); r++, roffset += (TermWin.ncol+1))
     {
	screen.text [roffset + TermWin.ncol] = '\0';
     }
}

/*
 * Insert or delete count lines and scroll
 * insdel == +1
 *	delete lines, scroll up the bottom of the screen to fill the gap
 * insdel == -1
 *	insert lines, scroll down the lower lines
 * other values of insdel are undefined
 */
void
scr_insdel_lines (int count, int insdel)
{
   check_text ("insdel lines");

   if (screen.row > screen.bscroll)
     return;

   if (count > (screen.bscroll - screen.row + 1))
     {
	if (insdel == DELETE)
	  return;
	else if (insdel == INSERT)
	  count = (screen.bscroll - screen.row + 1);
     }

   TermWin.hist_start = 0;
   if (graphics_up) Gr_scroll (0);

   if (screen.wrapnext)
     {
	int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);
	screen.text [roffset + (TermWin.ncol)] = '\0';
	screen.wrapnext = 0;
     }

   scroll_text (screen.row, screen.bscroll, insdel * count);
}

/*
 * insert or Delete count characters from the current position
 * insdel == +2, erase  chars
 * insdel == +1, delete chars
 * insdel == -1, insert chars
 */
void
scr_insdel_chars (int count, int insdel)
{
   int roffset;			/* row offset */
   text_t *text, *textend;
   rend_t *rend, *rendend;

   check_text ("insdel chars");

   if (insdel == ERASE)
     {
	if (count > screen.col)
	  count = screen.col;
	if (count <= 0)
	  return;
	screen.col -= count;	/* move backwards */
	insdel = DELETE;	/* delete chars */
     }
   else if (count > (TermWin.ncol - screen.col))
     {
	count = (TermWin.ncol - screen.col);
     }
   if (count <= 0)
     return;

   TermWin.hist_start = 0;
   if (graphics_up) Gr_scroll (0);
   if (selection.op) selection_check ();

   roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol+1);

   screen.text [roffset + (TermWin.ncol)] = '\0';
   screen.wrapnext = 0;

   text = &screen.text [roffset + (screen.col)];
   rend = &screen.rend [roffset + (screen.col)];
   if (insdel == DELETE)
     {
	/* overlapping copy */
	for (/*nil*/; (*text && text [count]); text++, rend++)
	  {
	     *text = text [count];
	     *rend = rend [count];
	  }

	/* fill in the end of the line */
	for (/*nil*/; *text; text++, rend++)
	  {
	     *text = SPACE_CHAR;
	     *rend = CURRENT_RSTYLE;
	  }
     }
   else
     {
	/* INSERT count characters */
	textend = &screen.text [roffset + (TermWin.ncol-1)];
	rendend = &screen.rend [roffset + (TermWin.ncol-1)];

	for (/*nil*/; (textend - count >= text); textend--, rendend--)
	  {
	     *textend = *(textend-count);
	     *rendend = *(rendend-count);
	  }

	/* fill in the gap */
	for (/*nil*/; (textend >= text); textend--, rendend--)
	  {
	     *textend = SPACE_CHAR;
	     *rendend = CURRENT_RSTYLE;
	  }
     }
}

/*
 * set the scroll region
 */
void
scr_scroll_region (int top, int bot)
{
   if (top < 0) top = 0;
   if (bot >= TermWin.nrow) bot = (TermWin.nrow-1);
   if (top > bot) return;

   screen.tscroll = top;
   screen.bscroll = bot;
   scr_gotorc (0, 0, 0);
}

/*
 * set/unset automatic wrapping
 */
void
scr_autowrap (int mode)
{
   screen.autowrap = mode;
}

/*
 * set/unset margin origin mode
 *
 * In absolute origin mode, line numbers are counted relative to top margin
 * of screen, the cursor can be moved outside the scrolling region. In
 * relative mode line numbers are relative to top margin of scrolling
 * region and the cursor cannot be moved outside
 */
void
scr_relative_origin (int mode)
{
   screen.relative = mode;
   scr_gotorc (0, 0, 0);
}

/*
 * set/unset automatic insert mode
 */
void
scr_insert_mode (int mode)
{
   screen.insert = mode;
}

/*
 * Move the display so that line represented by scrollbar value y is at
 * the top of the screen
 */
#ifndef NO_SCROLLBAR
int
scr_move_to (int y, int len)
{
   int start = TermWin.hist_start;

   TermWin.hist_start = ((len - y) * ((TermWin.nrow-1) + TermWin.hist_count)
			 / (len)) - (TermWin.nrow-1);

   if (TermWin.hist_start < 0)
     TermWin.hist_start = 0;
   else if (TermWin.hist_start > TermWin.hist_count)
     TermWin.hist_start = TermWin.hist_count;
   if (graphics_up)
     Gr_scroll (0);

   return (TermWin.hist_start - start);
}
#endif

/*
 * page the screen up/down NLINES
 */
int
scr_page (int dirn, int nlines)
{
   int start = TermWin.hist_start;

   if (!dirn || !nlines)
     return 0;

   if (nlines <= 0)
     nlines = 1;
   else if (nlines > TermWin.nrow)
     nlines = TermWin.nrow;
   TermWin.hist_start += nlines * dirn;

   if (TermWin.hist_start < 0)
     TermWin.hist_start = 0;
   else if (TermWin.hist_start > TermWin.hist_count)
     TermWin.hist_start = TermWin.hist_count;

   if (graphics_up)
     Gr_scroll (0);

   return (TermWin.hist_start - start);
}

/*
 * If (row,col) is within a selected region of text, remove the selection
 */
static inline void
selection_check (void)
{
   int c1, c2, r1, r2;
   check_text ("check selection");

   if (current_screen != selection.screen)
     return;

   if ((selection.mark.row < -TermWin.hist_count) ||
       (selection.mark.row >= TermWin.nrow) ||
       (selection.beg.row < -TermWin.hist_count) ||
       (selection.beg.row >= TermWin.nrow) ||
       (selection.end.row < -TermWin.hist_count) ||
       (selection.end.row >= TermWin.nrow))
     {
	selection_reset ();
	return;
     }

   r1 = (screen.row - TermWin.hist_start);
   c1 = ((r1 - selection.mark.row) * (r1 - selection.end.row));

   /* selection.mark.row > screen.row - TermWin.hist_start
    * or
    * selection.end.row > screen.row - TermWin.hist_start
    */
   if (c1 < 0)
     selection_reset ();
   /* selection.mark.row == screen.row || selection.end.row == screen.row */
   else if (c1 == 0)
     {
	/* We're on the same row as the start or end of selection */
	if ((selection.mark.row < selection.end.row) ||
	    ((selection.mark.row == selection.end.row) &&
	     (selection.mark.col < selection.end.col)))
	  {
	     r1 = selection.mark.row;
	     c1 = selection.mark.col;
	     r2 = selection.end.row;
	     c2 = selection.end.col;
	  }
	else
	  {
	     r1 = selection.end.row;
	     c1 = selection.end.col;
	     r2 = selection.mark.row;
	     c2 = selection.mark.col;
	  }
	if ((screen.row == r1) && (screen.row == r2))
	  {
	     if ((screen.col >= c1) && (screen.col <= c2))
	       selection_reset ();
	  }
	else if (((screen.row == r1) && (screen.col >= c1)) ||
		 ((screen.row == r2) && (screen.col <= c2)))
	  selection_reset ();
     }
}

#if 0
void
selection_range (int firstr, int lastr)
{
   if (firstr >= lastr ||
       firstr < 0 || firstr >= TermWin.nrow ||
       lastr <= 0 || lastr > TermWin.nrow)
    return;
   selection.firstr = firstr;
   selection.lastr = lastr;
}
#endif

/*
 * make the selection currently delimited by the selection end markers
 */
void
selection_make (Time time)
{
   unsigned char *str;
   int r, startr, startc, endr, endc;
   int roffset;			/* row offset */

   switch (selection.op) {
    case SELECTION_CONT:
      break;

    case SELECTION_INIT:
      selection_reset ();
      selection.end.row = selection.mark.row = selection.beg.row;
      selection.end.col = selection.mark.col = selection.beg.col;
      /*drop*/
    case SELECTION_BEGIN:
      selection.op = SELECTION_DONE;
      /*drop*/
    default:
      return;
      break;
   }
   selection.op = SELECTION_DONE;

   FREE (selection.text, "sel_make", "sel_make");
   selection.text = NULL;
   selection.len = 0;

   selection.screen = current_screen;
   /* Set start/end row/col to point to the selection endpoints */
   if (selection.end.row < selection.mark.row ||
       (selection.end.row == selection.mark.row &&
	selection.end.col <= selection.mark.col))
     {
	startr = selection.end.row; endr = selection.mark.row;
	startc = selection.end.col; endc = selection.mark.col;
     }
   else
     {
	startr = selection.mark.row; endr = selection.end.row;
	startc = selection.mark.col; endc = selection.end.col;
     }

   if ((startr < -TermWin.hist_count || endr >= TermWin.nrow))
     {
	selection_reset ();
	return;
     }

   str = MALLOC (((endr - startr + 1)*(TermWin.ncol+1) + 1) * sizeof(char),
		 "sel_text");
   selection.text = str;
   *str = '\0';

   /* save all points between start and end with selection flag */
   roffset = ((startr + TermWin.saveLines) * (TermWin.ncol+1));
   for (r = startr; r <= endr; r++, roffset += (TermWin.ncol+1))
     {
	int c1, c2;
	c1 = (r == startr ? startc : 0);
	c2 = (r == endr ? endc : (TermWin.ncol-1));

#ifdef KANJI
	if ((screen.rend [roffset+c1] & RS_kanjiMask) == RS_kanjiMask) c1--;
	if ((screen.rend [roffset+c2] & RS_kanjiMask) == RS_kanji1) c2++;
#endif	/* KANJI */
	for (/*nil*/; c1 <= c2; c1++)
	  *str++ = screen.text [roffset + c1];

	if (c2 == (TermWin.ncol-1))
	  {
	     /* not autowrap */
	     if (!screen.text [roffset + (TermWin.ncol)])
	       {
		  str--;
		  /* remove trailing space */
		  while ((str >= selection.text) && isspace (*str))
		    str--;
		  str++;
		  *str = '\n';
		  str++;
	       }
	  }
     }
   *str = '\0';

   selection.len = strlen (selection.text);
   if (selection.len <= 0)
     return;
   XSetSelectionOwner (Xdisplay, XA_PRIMARY, TermWin.vt, time);
   if (XGetSelectionOwner (Xdisplay, XA_PRIMARY) != TermWin.vt)
     print_error ("can't get primary selection");

   /* Place in CUT_BUFFER0 for backup */
   XChangeProperty (Xdisplay, DefaultRootWindow (Xdisplay), XA_CUT_BUFFER0,
		    XA_STRING, 8, PropModeReplace,
		    selection.text, selection.len);
}

/*
 * respond to a request for our current selection
 */
void
selection_send (XSelectionRequestEvent *rq)
{
   XEvent event;

   event.xselection.type	= SelectionNotify;
   event.xselection.display	= rq->display;
   event.xselection.requestor	= rq->requestor;
   event.xselection.selection	= rq->selection;
   event.xselection.target	= rq->target;
   event.xselection.property	= None;
   event.xselection.time	= rq->time;

   if (rq->target == XA_STRING)
     {
	XChangeProperty (Xdisplay, rq->requestor, rq->property,
			 XA_STRING, 8, PropModeReplace,
			 selection.text, selection.len);
	event.xselection.property = rq->property;
     }
   XSendEvent (Xdisplay, rq->requestor, False, 0, &event);
}

static void
PasteIt (unsigned char * data, int nitems)
{
   unsigned char *p, *pmax = data + nitems;

   for (nitems = 0, p = data; p < pmax; p++)
     {
	if (*p == '\n')
	  {
	     unsigned char cr = '\r';
	     tty_write (data, nitems);
	     tty_write (&cr, 1);
	     data += (nitems+1);
	     nitems = 0;
	  }
	else
	  nitems++;
     }
   if (nitems)
     tty_write (data, nitems);
#if 0
   for (p = data; p < pmax; p++)
     if (*p == '\n') *p = '\r';
   tty_write (data, nitems);
#endif
}

/*
 * Respond to a notification that a primary selection has been sent
 */
void
selection_paste (Window win, int prop, int Delete)
{
   long nread, bytes_after;

   if (prop == None)
     return;

   nread = 0;
   do
     {
	unsigned char * data;
	Atom actual_type;
	int actual_fmt;
	long nitems;

	if ((XGetWindowProperty (Xdisplay, win, prop,
				 nread/4, PROP_SIZE, Delete,
				 AnyPropertyType, &actual_type, &actual_fmt,
				 &nitems, &bytes_after,
				 &data) != Success) ||
	    (actual_type != XA_STRING))
	  {
	     XFree (data);
	     return;
	  }

	nread += nitems;
	PasteIt (data, nitems);

	XFree (data);
     }
   while (bytes_after > 0);
}

/*
 * Request the current primary selection
 */
void
selection_request (Time time, int x, int y)
{
   Atom prop;

   /* is release within the window? */
   if (x < 0 || y < 0 || x >= TermWin.width || y >= TermWin.height)
     return;

   if (selection.text != NULL)		/* selection is internal */
     {
	PasteIt (selection.text, selection.len);
	return;
     }

   if (XGetSelectionOwner (Xdisplay, XA_PRIMARY) == None)
     {
	/* No primary selection so use the cut buffer */
	selection_paste (DefaultRootWindow (Xdisplay),
			 XA_CUT_BUFFER0, False);
	return;
     }
   prop = XInternAtom (Xdisplay, "VT_SELECTION", False);
   XConvertSelection (Xdisplay, XA_PRIMARY, XA_STRING,
		      prop, TermWin.vt, time);
}

/*
 * Clear the current selection
 */
void
selection_reset (void)
{
   int x, nrow;

   selection.op = SELECTION_CLEAR;
   selection.end.row = selection.mark.row = 0;
   selection.end.col = selection.mark.col = 0;
   nrow = TermWin.nrow;
   if (current_screen == PRIMARY)
     nrow += TermWin.saveLines;

   for (x = 0; x < nrow * (TermWin.ncol+1); x++)
     screen.rend [x] &= ~(RS_Select);
}

void
selection_clear (void)
{
   FREE (selection.text, "sel_clear", "sel_clear");
   selection.text = NULL;
   selection.len = 0;

   selection.op = SELECTION_CLEAR;
   selection_reset ();
}

/*
 * mark selected points (used by selection_extend)
 */
static void
selection_setclr (int set, int startr, int startc, int endr, int endc)
{
   int r, roffset;

   /* startr <= endr */
   if ((startr < -TermWin.hist_count) || (endr >= TermWin.nrow))
     {
	selection_reset ();
	return;
     }
   roffset = ((startr + TermWin.saveLines) * (TermWin.ncol+1));

   for (r = startr; r <= endr; r++)
     {
	int c1, c2;
	c1 = (r == startr ? startc : 0);
	c2 = (r == endr ? endc : (TermWin.ncol-1));

#ifdef KANJI
	if ((screen.rend [roffset+c1] & RS_kanjiMask) == RS_kanjiMask) c1--;
	if ((screen.rend [roffset+c2] & RS_kanjiMask) == RS_kanji1) c2++;
#endif	/* KANJI */
	for (/*nil*/; c1 <= c2; c1++)
	  {
	     if (set)
	       screen.rend [roffset + c1] |= RS_Select;
	     else
	       screen.rend [roffset + c1] &= ~(RS_Select);
	  }
	roffset += (TermWin.ncol+1);
     }
}

/*
 * start a selection using the specified unit
 */
void
selection_start (int x, int y)
{
   if (selection.op)
     {
	/* startr <= endr */
	if ((selection.end.row < -TermWin.hist_count) ||
	    (selection.mark.row < - TermWin.hist_count))
	  {
	     selection_reset ();
	  }
	else			/* direction of new selection */
	  if (selection.end.row < selection.mark.row ||
	      (selection.end.row == selection.mark.row &&
	       selection.end.col <= selection.mark.col))
	    selection_setclr (0,    /* up */
			      selection.end.row, selection.end.col,
			      selection.mark.row, selection.mark.col);
	else
	  selection_setclr (0,	/* down */
			    selection.mark.row, selection.mark.col,
			    selection.end.row, selection.end.col);
     }
   selection.op = SELECTION_INIT;

   selection.beg.col = Pixel2Col (x);
   selection.beg.row = Pixel2Row (y);
   selection.beg.row -= TermWin.hist_start;
}

/*
 * extend the selection
 */
void
selection_extend (int hilite, int x, int y)
{
   int old_row, old_col, old_dirn, dirn;

   switch (selection.op) {
    case SELECTION_INIT:
      selection_reset ();
      selection.end.col = selection.mark.col = selection.beg.col;
      selection.end.row = selection.mark.row = selection.beg.row;
      /*drop*/
    case SELECTION_BEGIN:
      selection.op = SELECTION_BEGIN;
      break;

    case SELECTION_DONE:
    case SELECTION_CONT:
      selection.op = SELECTION_CONT;
      break;

    case SELECTION_CLEAR:
      selection_start (x, y);
      /*drop*/
    default:
      return;
      break;
   }

   /* Remember old selection for virtual removal */
   old_row = selection.end.row;
   old_col = selection.end.col;

   if ((old_row < -TermWin.hist_count) ||
       (selection.mark.row < -TermWin.hist_count))
     {
	selection_reset ();
	return;
     }

   /* Figure out where new selection is */
   selection.end.col = Pixel2Col (x);
   selection.end.row = Pixel2Row (y);

   if (selection.end.col < 0) selection.end.col = 0;
   else if (selection.end.col >= TermWin.ncol)
     selection.end.col = (TermWin.ncol-1);

   selection.end.row -= TermWin.hist_start;
   if (selection.end.row < -TermWin.hist_count)
     {
	selection_reset ();
	return;
     }
   else if (selection.end.row >= TermWin.nrow)
     selection.end.row = (TermWin.nrow-1);

   if ((selection.op == SELECTION_BEGIN) &&
       ((selection.end.col != selection.mark.col) ||
	(selection.end.row != selection.mark.row)))
     selection.op = SELECTION_CONT;

   /* If new selection is same as old selection just return
    * or if no highlighting was requested
    */
   if (!hilite ||
       (selection.end.row == old_row && selection.end.col == old_col))
     return;

   /* virtual removal -- delete old highlighting and replace with new */

   /* determine direction of old selection */
   old_dirn = ((old_row < selection.mark.row ||
		(old_row == selection.mark.row &&
		 old_col <= selection.mark.col)) ? UP : DOWN);

   /* determine direction of new selection */
   dirn = ((selection.end.row < selection.mark.row ||
	    (selection.end.row == selection.mark.row &&
	     selection.end.col <= selection.mark.col)) ? UP : DOWN);

   /* If old and new direction are different, clear old, set new */
   if (dirn != old_dirn)
     {
	if (old_dirn == UP)
	  {
	     selection_setclr (0,
			       old_row, old_col,
			       selection.mark.row, selection.mark.col);
	     selection_setclr (1,
			       selection.mark.row, selection.mark.col,
			       selection.end.row, selection.end.col);
	  }
	else
	  {
	     selection_setclr (0,
			       selection.mark.row, selection.mark.col,
			       old_row, old_col);
	     selection_setclr (1,
			       selection.end.row, selection.end.col,
			       selection.mark.row, selection.mark.col);
	  }
     }
   else
     {
	if (old_dirn == UP)
	  {
	     if (old_row < selection.end.row ||
		 (old_row == selection.end.row &&
		  old_col < selection.end.col))
	       {
		  selection_setclr (0,
				    old_row, old_col,
				    selection.end.row, selection.end.col);
		  selection_setclr (1,
				    selection.end.row, selection.end.col,
				    selection.end.row, selection.end.col);
	       }
	     else
	       {
		  selection_setclr (1,
				    selection.end.row, selection.end.col,
				    old_row, old_col);
	       }
	  }
	else
	  {
	     if (selection.end.row < old_row ||
		 (selection.end.row == old_row &&
		  selection.end.col < old_col))
	       {
		  selection_setclr (0,
				    selection.end.row, selection.end.col,
				    old_row, old_col);
		  selection_setclr (1,
				    selection.end.row, selection.end.col,
				    selection.end.row, selection.end.col);
	       }
	     else
	       {
		  selection_setclr (1,
				    old_row, old_col,
				    selection.end.row, selection.end.col);
	       }
	  }
     }
}

/*
 * double/triple click selection, by Edward. Der-Hua Liu, Taiwan
 * Added cut char class support: A. Haritsis <ah@doc.ic.ac.uk>
 *
 * could be nice to handle autowrapped lines
 */
void
selection_click (int clicks, int x, int y)
{
#ifndef NO_MULTIPLE_CLICK
   if (clicks > 1)
     {
	int startx, endx;

	switch (clicks) {
	 case 2:			/* double click */
	   /* equivalent text column */
	   x = Pixel2Col (x);
	   if (x < 0)
	     x = 0;
	   else if (x >= TermWin.ncol)
	     x = (TermWin.ncol-1);
	   startx = endx = x;

	   /* equivalent text row */
	   x = Pixel2Row (y);
	   if (x < 0)
	     x = 0;
	   else if (x >= TermWin.nrow)
	     x = (TermWin.nrow-1);

	   /* starting offset */
	   x = (x + TermWin.saveLines - TermWin.hist_start) * (TermWin.ncol+1);

	   while (startx > 0 &&
		  !strchr (rs_cutchars, screen.text [x+startx-1]))
	     startx--;
	   while (endx < (TermWin.ncol-1) &&
		  !strchr (rs_cutchars, screen.text [x+endx+1]))
	     endx++;
	   break;

	 case 3:			/* triple click */
	   startx = 0;
	   endx = (TermWin.ncol-1);
	   break;

	 default:
	   return;
	   break;
	}
	selection_start (Col2Pixel (startx), y);
	selection_extend (1, Col2Pixel (endx), y);
     }
   else
#endif	/* NO_MULTIPLE_CLICK */
     selection_start (x, y);	/* single click */
}

/*
 * Report the current cursor position
 */
void
scr_report_position (void)
{
   tty_printf ("\033[%d;%dR", screen.row + 1, screen.col + 1);
}

/*
 * choose a font
 */
void
scr_charset_choose (int set)
{
   screen.charset = set;
   set_font_style ();
}

/*
 * Set a font
 */
void
scr_charset_set (int set, unsigned char ch)
{
#ifdef KANJI
   multiByte = (set < 0);
   set = abs (set);
#endif
   charsets [set] = ch;
   set_font_style ();
}

/*
 * for the box starting at x, y with size width, height
 * touch the displayed values
 */
void
scr_touch (int x, int y, int width, int height)
{
   int row, col, end_row, end_col;

   check_text ("touch");

   col = Pixel2Col (x);
   row = Pixel2Row (y);
   if (col < 0) col = 0; else if (col >= TermWin.ncol) col = (TermWin.ncol-1);
   if (row < 0) row = 0; else if (row >= TermWin.nrow) row = (TermWin.nrow-1);
   end_col = col + 1 + width  / TermWin.fwidth;
   end_row = row + 1 + height / TermWin.fheight;

   if (end_row >= TermWin.nrow) end_row = (TermWin.nrow-1);
   if (end_col >= TermWin.ncol) end_col = (TermWin.ncol-1);

   width = (end_col - col + 1);
   for (/*nil*/; row <= end_row; row++)
     {
	int roffset = col + row * (TermWin.ncol+1);
	memset (&drawn_text [roffset], 0, width * sizeof(text_t));
	memset (&drawn_rend [roffset], 0, width * sizeof(rend_t));
     }
}

/*
 * refresh the region defined by rows STARTR and ENDR, inclusively.
 *
 * Actually draws to the X window
 * For X related speed-ups, this is a good place to fiddle.
 * The arrays drawn_text and drawn_rend contain what I
 * believe is currently shown on the screen. The arrays in screen contain
 * what should be displayed. This routine can decide how to refresh the
 * screen. Calls in command.c decide when to refresh.
 */

void
scr_refresh_region (int startr, int endr, int type)
{
   static int last_xcursor = 0;
   int r, roffset, doffset, xcursor;
   int ypixel;
#ifdef KANJI
   static int wbyte = 0;
#endif

   /* fix up startr/endr order */
   if (startr > endr)
     {
	r = startr; startr = endr; endr = r;
     }

   if (type == NO_REFRESH	/* Window not visible, don't update */
       || startr < 0 || endr >= TermWin.nrow)
     return;

   check_text ("refresh region");

   if (last_xcursor < (TermWin.nrow * (TermWin.ncol+1)))
     {
	/* make sure to update it */
   	drawn_rend [last_xcursor] = RS_attrMask;
     }

   xcursor = ((screen.row + TermWin.saveLines)*(TermWin.ncol+1)
	      + screen.col);
   last_xcursor = (screen.row + TermWin.hist_start);
   if (last_xcursor >= TermWin.nrow)
     {
	last_xcursor = 0;
     }
   else
     {
	last_xcursor *= (TermWin.ncol+1);
	if (screen.cursor_visible)
	  {
	     screen.rend [xcursor] |= RS_Cursor;
#ifdef KANJI
	     if ((screen.rend [xcursor] & RS_kanjiMask) == RS_kanji1)
	       screen.rend [xcursor+1] |= RS_Cursor;
#endif
	  }
     }
   last_xcursor += screen.col;

#ifdef USE_XCOPYAREA
   /*
    * scroll using bitblt wherever possible
    * a dirty approximation will ignore the rendition field here
    * and fix it up later
    */
   if (type == FAST_REFRESH && !graphics_up)
     for (r = startr; r <= endr; r++)
     {
	int k, roffset, doffset;

	doffset = r * (TermWin.ncol+1);
	roffset = doffset + ((TermWin.saveLines - TermWin.hist_start) *
			     (TermWin.ncol+1));

	if (!memcmp (&drawn_text [doffset],
		     &screen.text [roffset],
		     (TermWin.ncol) * sizeof(text_t)))
	  continue;

	/*
	 * look for a similar line
	 */
	doffset = startr * (TermWin.ncol+1);
	for (k = startr; k <= endr; k++)
	  {
	     if (!memcmp (&drawn_text [doffset],
			  &screen.text [roffset],
			  TermWin.ncol * sizeof(text_t))
		 )
	       break;
	     doffset += (TermWin.ncol + 1);
	  }
	/* found it */
	if (k <= endr)
	  {
	     int count, j;

	     j = r;
	     roffset += (TermWin.ncol+1);
	     doffset += (TermWin.ncol+1);
	     r++;
	     for (count = 1;
		  ((r <= endr) &&
		   !memcmp (&drawn_text [doffset],
			    &screen.text [roffset],
			    TermWin.ncol * sizeof(text_t))
		   );
		  count++, r++)
	       {
		  roffset += (TermWin.ncol+1);
		  doffset += (TermWin.ncol+1);
	       }
	     r--;
	     XCopyArea (Xdisplay, TermWin.vt, TermWin.vt, TermWin.gc,
			TermWin_internalBorder, Row2Pixel (k),
			TermWin.width,
			count * TermWin.fheight,
			TermWin_internalBorder, Row2Pixel (j));

	     /*
	      * Forward overlapping memcpy's are probably OK,
	      * but backwards doesn't work on SunOS 4.1.3
	      */
	     k *= (TermWin.ncol+1);
	     j *= (TermWin.ncol+1);
	     if (k > j)
	       {
		  while (count-- > 0)
		    {
		       MEMCOPY (&drawn_text [j],
				&drawn_text [k],
				count * (TermWin.ncol+1) * sizeof(text_t));
		       MEMCOPY (&drawn_rend [j],
				&drawn_rend [k],
				count * (TermWin.ncol+1) * sizeof(rend_t));
		       k += (TermWin.ncol+1);
		       j += (TermWin.ncol+1);
		    }
	       }
	     else
	       {
		  k += (count-1) * (TermWin.ncol+1);
		  j += (count-1) * (TermWin.ncol+1);
		  while (count-- > 0)
		    {
		       MEMCOPY (&drawn_text [j],
				&drawn_text [k],
				(TermWin.ncol+1) * sizeof(text_t));
		       MEMCOPY (&drawn_rend [j],
				&drawn_rend [k],
				(TermWin.ncol+1) * sizeof(rend_t));
		       k -= (TermWin.ncol+1);
		       j -= (TermWin.ncol+1);
		    }
	       }
	  }
     }
#endif	/* USE_XCOPYAREA */

   doffset = startr * (TermWin.ncol+1);
   roffset = doffset + ((TermWin.saveLines - TermWin.hist_start) * (TermWin.ncol+1));
   ypixel  = TermWin.font->ascent + Row2Pixel (startr);

   /* For a first cut, do it one character at a time */
   for (r = startr;
	r <= endr;
	roffset += (TermWin.ncol+1),
	doffset += (TermWin.ncol+1),
	ypixel += (TermWin.fheight),
	r++)
     {
	int c;

#ifndef KANJI
	/* fast way to avoid the next loop (most of the time) ? */
	if (!memcmp (&drawn_text [doffset],
		     &screen.text [roffset],
		     (TermWin.ncol) * sizeof(text_t)) &&
	    !memcmp (&drawn_rend [doffset],
		     &screen.rend [roffset],
		     (TermWin.ncol) * sizeof(rend_t)))
	  continue;
#endif

	for (c = 0; c < TermWin.ncol; c++)
	  {
	     int x, x1, count;

	     x = roffset + c;
	     x1 = doffset + c;
	     if ((drawn_text [x1] != screen.text [x]) ||
		 (drawn_rend [x1] != screen.rend [x])
#ifdef KANJI
		 || (((screen.rend [x] & RS_kanjiMask) == RS_kanji1) &&
		     (drawn_text [x1+1] != screen.text [x+1]))
#endif	/* KANJI */
		 )
	       {
		  int fore, back, rend;
		  XGCValues gcvalue;		/* GC values */
		  unsigned long gcmask = 0;	/* GC mask */
		  int outline = False;
		  int xpixel = Col2Pixel (c);

		  drawn_text [x1] = screen.text [x];
		  drawn_rend [x1] = screen.rend [x];
		  linebuf [0] = screen.text [x];
		  rend = screen.rend [x];

		  x++; c++;
		  for (count = 1;
		       (c < TermWin.ncol &&
#ifdef KANJI
			((rend & ~RS_kanji0) ==
			 (screen.rend [x] & ~RS_kanji0)) &&
#else	/* KANJI */
			(rend == screen.rend [x]) &&
#endif	/* KANJI */
			(drawn_text [doffset+c] != screen.text [x] ||
			 drawn_rend [doffset+c] != screen.rend [x] ||
#ifdef KANJI
			 (x > 0 && (screen.rend [x-1] & ~RS_kanji1)) ||
#endif	/* KANJI */
			 (c+1 < TermWin.ncol &&
			  drawn_text [doffset+c+1] != screen.text [x+1])));
		       count++, x++, c++)
		    {
		       drawn_text [doffset+c] = screen.text [x];
		       drawn_rend [doffset+c] = screen.rend [x];
		       linebuf [count] = screen.text [x];
		    }
		  c--;

#ifdef KANJI
		  /* ensure the correct font is used */
		  if (rend & RS_kanji1)
		    {
		       if (!wbyte)
			 {
			    wbyte = 1;
			    XSetFont (Xdisplay, TermWin.gc, TermWin.kanji->fid);
			 }

		       if (linebuf [0] & 0x80)
			 kanji_decode (linebuf, count);
		    }
		  else
		    {
		       if (wbyte)
			 {
			    wbyte = 0;
			    XSetFont (Xdisplay, TermWin.gc, TermWin.font->fid);
			 }
		    }
#endif	/* KANJI */

		  fore = GET_FGCOLOR (rend);
		  back = GET_BGCOLOR (rend);
		  rend = GET_ATTR (rend);
		  if (rend)
		    {
		       int rvid = 0;

		       if ((rend & (RS_RVid|RS_Select)) == (RS_RVid|RS_Select))
			 rend &= ~(RS_RVid|RS_Select);
		       else if (rend & (RS_RVid|RS_Select))
			 rvid = 1;

		       if (rend & RS_Cursor)
			 {
			    if (!TermWin.focus)
			      {
				 outline = True;
				 rend &= ~(RS_Cursor);
			      }
			    else
			      rvid = (!rvid
#ifndef NO_CURSORCOLOR
				      || color_cursor
#endif
				      );
			 }

		       if (rvid)    /* swap foreground/background colors */
			 {
			    rvid = back;
			    back = fore;
			    fore = rvid;
			 }

		       /*
			* do some font character switching
			*/
		       switch (rend & RS_fontMask) {
			case RS_acsFont:
			  for (x = 0; x < count; x++)
			    if (linebuf [x] >= 0x5F && linebuf [x] < 0x7F)
			      linebuf [x] = (linebuf [x] == 0x5F ?
					     0x7F : linebuf [x] - 0x5F);
			  break;
			case RS_ukFont:
			  for (x = 0; x < count; x++)
			    if (linebuf [x] == '#') linebuf [x] = '\036';
			  break;
		       }
		    }

		  if (fore != COLORFG)
		    {
		       gcvalue.foreground = pixel_colors [fore];
		       gcmask |= GCForeground;
#ifdef DEBUG_REFRESH
		       fprintf (stderr, "set fg=%d\n", fore);
#endif
		    }
		  if (back != COLORBG)
		    {
		       gcvalue.background = pixel_colors [back];
		       gcmask |= GCBackground;
#ifdef DEBUG_REFRESH
		       fprintf (stderr, "set bg=%d\n", back);
#endif
		    }

#ifndef NO_CURSORCOLOR
		  if ((rend & RS_Cursor) && color_cursor)
		    {
		       gcvalue.background = pixel_colors [CURSORCOLOR];
		       gcmask |= GCBackground;
# ifndef NO_CURSORCOLOR2
		       if (color_cursor & RS_fgMask)
			 {
			    gcvalue.foreground = pixel_colors [CURSORCOLOR2];
			    gcmask |= GCForeground;
			 }
# endif	/* NO_CURSORCOLOR2 */
		    }
#endif	/* NO_CURSORCOLOR */

		  if (gcmask)
		    XChangeGC (Xdisplay, TermWin.gc, gcmask, &gcvalue);

#ifdef KANJI
		  if (wbyte)
		    XDrawImageString16 (Xdisplay, TermWin.vt, TermWin.gc,
					xpixel, ypixel,
					(XChar2b *)linebuf, count/2);
		  else
#endif	/* KANJI */
		    XDrawImageString (Xdisplay, TermWin.vt, TermWin.gc,
				      xpixel, ypixel,
				      linebuf, count);
#ifdef DEBUG_REFRESH
		  /* fprintf(stderr, "@%d '%.*s'\n", __LINE__, count, linebuf); */
#endif

		  if (
#ifdef NO_BOLDCOLOR
		      (rend & (RS_Bold|RS_Blink))
#else	/* NO_BOLDCOLOR */
		      ((rend & RS_Bold)  && fore == COLORFG)
		      /* || ((rend & RS_Blink) && back == COLORBG) */
		      /*
		       * blink simulated by simulated bold
		       * (overstrike) seems a bit farfetched
		       *
		       * comment this out (v2.17 - 04APR96)
		       * and see how many people complain
		       */
#endif	/* NO_BOLDCOLOR */
		      )
		    {
#ifdef KANJI
		       if (wbyte)
			 XDrawString16 (Xdisplay, TermWin.vt, TermWin.gc,
					xpixel + 1, ypixel,
					(XChar2b *)linebuf, count/2);
		       else
#endif	/* KANJI */
			 XDrawString (Xdisplay, TermWin.vt, TermWin.gc,
				      xpixel + 1, ypixel,
				      linebuf, count);
		    }

		  /*
		   * On the smallest font,
		   * underline overwrites the next row
		   */
		  if ((rend & RS_Uline) && (TermWin.font->descent > 1))
		    XDrawLine (Xdisplay, TermWin.vt, TermWin.gc,
			       xpixel,
			       ypixel + 1,
			       xpixel + count * TermWin.fwidth - 1,
			       ypixel + 1);

		  if (outline)
		    {
#ifndef NO_CURSORCOLOR
		       if (color_cursor)
			 {
			    gcvalue.foreground = pixel_colors [CURSORCOLOR];
			    gcmask |= GCForeground;
			    XChangeGC (Xdisplay, TermWin.gc, gcmask, &gcvalue);
			 }
#endif	/* NO_CURSORCOLOR */
		       XDrawRectangle (Xdisplay, TermWin.vt, TermWin.gc,
				       xpixel,
				       ypixel - TermWin.font->ascent,
				       TermWin.fwidth - 1,
				       TermWin.fheight - 1);
		    }
		  if (gcmask)
		    {
		       /* restore normal colors */
		       gcvalue.foreground = pixel_colors [COLORFG];
		       gcvalue.background = pixel_colors [COLORBG];
		       XChangeGC (Xdisplay, TermWin.gc, gcmask, &gcvalue);
		    }
	       }
	  }
     }
   if (screen.cursor_visible)
     {
	screen.rend [xcursor] &= ~(RS_Cursor);
#ifdef KANJI
	screen.rend [xcursor+1] &= ~(RS_Cursor);
#endif	/* KANJI */
     }
}

void
scr_refresh (int type)
{
   scr_refresh_region (0, (TermWin.nrow-1), type);
}

/*
 *  -1 = clear all tabs
 *  +0 = clear tab stop at current column
 *  +1 = set   tab stop at current column
 */
void
scr_set_tab (int mode)
{
   if (mode < 0)
     memset (tabs, 0, TermWin.ncol * sizeof(char));
   else if (screen.col < TermWin.ncol)
     tabs [screen.col] = (mode != 0);
}

/*
 * toggle reverse video settings
 */
void
scr_rvideo_mode (int mode)
{
   if (rvideo != mode)
     {
	register int x, count;

	rvideo = mode;
	rstyle ^= RS_RVid;

	count = (TermWin.nrow + TermWin.saveLines) * (TermWin.ncol+1);
	for (x = 0; x < count; x++) screen.rend [x] ^= RS_RVid;
	scr_refresh (SLOW_REFRESH);

#ifdef DEBUG_COLORS
	debug_colors ();
#endif
     }
}

/*
 * Handle receipt of a bell character
 */
void
scr_bell (void)
{
#ifndef NO_MAPALERT
#ifdef MAPALERT_OPTION
   if (Options & Opt_mapAlert)
#endif
     XMapWindow (Xdisplay, TermWin.parent);
#endif

   if (Options & Opt_visualBell)
     {
	scr_rvideo_mode (!rvideo);
	scr_rvideo_mode (!rvideo);
     }
   else
     XBell (Xdisplay, 0);
}

#ifdef PRINTPIPE
void
scr_printscreen (int fullhist)
{
   text_t *text;
   int r, nrows;
   FILE *fd;

   if ((fd = popen_printer ()) == NULL)
     return;

   nrows = TermWin.nrow;
   if (fullhist)
     nrows += TermWin.hist_start;

   text = &screen.text [((TermWin.saveLines - TermWin.hist_start) *
			 (TermWin.ncol+1))];
   for (r = 0; r < nrows; r++, text += (TermWin.ncol+1))
     {
	int i;
	for (i = (TermWin.ncol-1); i >= 0 && isspace (text [i]); i--) /*nil*/;
	i++;
	fprintf (fd, "%.*s\n", i, text);
     }

   pclose_printer (fd);
}
#endif

/*
 * hidden debugging dump
 */
#ifdef DEBUG_SELECTION
static void
debug_PasteIt (unsigned char * data, int nitems)
{
   unsigned char *p, *pmax = data + nitems;
   int i;
   printf ("<text>\n\"");

   for (nitems = 0, p = data; p < pmax; p++)
     {
	if (*p == '\n')
	  {
	     printf ("[%d chars]\n", nitems);
	     printf ("%.*s\\n\n", nitems, data);
	     data += (nitems+1);
	     nitems = 0;
	  }
	else if (*p == '\r')
	  {
	     printf ("[%d chars]\n", nitems);
	     printf ("%.*s\\r\n", nitems, data);
	     data += (nitems+1);
	     nitems = 0;
	  }
	else
	  nitems++;
     }
   if (nitems)
     printf ("%*s", (nitems), data);
   printf ("\"\n</text>");
}

int
debug_selection (void)
{
   printf ("\n%dx%d [chars] %dx%d [pixels] (font: %ux%u), scroll = %d/%d\n",
	   TermWin.ncol,   TermWin.nrow,
	   TermWin.width,  TermWin.height,
	   TermWin.fwidth, TermWin.fheight,
	   screen.tscroll, screen.bscroll);

   printf ("%d lines scrollback, %d lines scrolled, start = %d\n",
	   TermWin.saveLines, TermWin.hist_count, TermWin.hist_start);

   printf ("selection = screen %d, op = ", selection.screen);

   switch (selection.op) {
    case SELECTION_CLEAR:	printf ("CLEAR");	break;
    case SELECTION_BEGIN:	printf ("BEGIN");	break;
    case SELECTION_INIT:	printf ("INIT");	break;
    case SELECTION_CONT:	printf ("CONT");	break;
    case SELECTION_DONE:	printf ("DONE");	break;
    default:			printf ("Unknown");	break;
   }
   printf ("\n\trow/col\n"
	   "beg\t%d %d\nend\t%d %d\nanchor\t%d %d\ncursor\t%d %d\n"
	   "selection [%d chars]\n",
	   selection.beg.row, selection.beg.col,
	   selection.end.row, selection.end.col,
	   selection.mark.row, selection.mark.col,
	   screen.row, screen.col,
	   selection.len);

   if (selection.text != NULL)
     debug_PasteIt (selection.text, selection.len);

   return 0;
}
#endif

/* add the bits: 1 - shift, 2 - meta, 4 - ctrl */
#define KeyState(x) (((x)&(ShiftMask|ControlMask))+(((x) & Mod1Mask)? 2 : 0))

#ifndef NO_MOUSE_REPORT
void
mouse_report (XButtonEvent *ev)
{
   int but, col, row;

   but = (040 + ((ev->button ? (ev->button - Button1) : 3)
		 + (KeyState (ev->state) << 2)));
   col = 041 + Pixel2Col (ev->x);
   row = 041 + Pixel2Row (ev->y);

   tty_printf ("\033[M%c%c%c", but, col, row);
}

# if 0	/* X11 mouse tracking: not yet - maybe never! */
void
mouse_tracking (int report, int x, int y, int firstrow, int lastrow)
{
   static int startx, starty, top, bot;

   if (report)
     {
	/* If either coordinate is past the end of the line:
	 * "ESC [ T CxCyCxCyCxCy"
	 * The parameters are startx, starty, endx, endy,
	 * mousex, and mousey */

	if ((selection.beg.row < selection.end.row) ||
	    ((selection.beg.row == selection.end.row) &&
	     (selection.beg.col < selection.end.col)))
	  {
	     if (selection.beg.row >= top && selection.end.row <= bot)
	       tty_printf ("\033[t"); /* start/end are valid locations */
	     else
	       tty_printf ("\033[T%c%c%c%c",
			   selection.beg.col + 1, selection.beg.row + 1,
			   selection.end.col + 1, selection.end.row + 1);
	  }
	else
	  {
	     if (selection.end.row >= top && selection.beg.row <= bot)
	       tty_printf ("\033[t");	/* start/end are valid locations */
	     else
	       tty_printf ("\033[T%c%c%c%c",
			   selection.end.col + 1, selection.end.row + 1,
			   selection.beg.col + 1, selection.beg.row + 1)
	  }
	tty_printf ("%c%c", Pixel2Col (x) + 1, Pixel2Row (y) + 1);
     }
   else
     {
	/* convert column/row to pixels */
	x = Col2Pixel (x - 1);
	y = Row2Pixel (y - 1);

	selection_start (x ,y);
	top = firstrow;
	bot = lastrow;
     }
}
# endif
#endif
/*----------------------- end-of-file (C source) -----------------------*/
