/*--------------------------------*-C-*---------------------------------*
 * File:	screen.c
 */
/*{{{ notes: */
/*----------------------------------------------------------------------*
 * 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.
 *----------------------------------------------------------------------*/
/*}}} */
/*{{{ includes */
#include "main.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/Xatom.h>
#include <X11/Xmd.h>		/* CARD32 */

#include "command.h"
#include "debug.h"
#include "graphics.h"
#include "screen.h"
/*}}} */
/*{{{ defines */
#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_BRIGHTCOLOR
unsigned int colorfgbg = DEFAULT_RSTYLE;
#endif
/*}}} */

/* #define DEBUG_SCREEN */
/* to do self-check for internal screen corruption */
/* #define DEBUG_COLORS */
/* to print out current color/renditions as they change */

/*{{{ local variables */
/*----------------------------------------------------------------------*
 */
typedef unsigned char text_t;
typedef unsigned int rend_t;

static rend_t rstyle = DEFAULT_RSTYLE;

#define SPACE_CHAR ' '
#ifdef OLD_COLOR_MODEL
#define FILL_STYLE	DEFAULT_RSTYLE
#else
#define FILL_STYLE	(rstyle)
#endif

/* macro prototypes */
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 n = (nlines) * (TermWin.ncol+1);\
if (n > 0) {\
memset ((ptext), 0, (n * sizeof(text_t)));\
memset ((prend), 0, (n * sizeof(rend_t)));\
}} while (0)

/* fill text with spaces and fill rendition */
#define	BLANK_FILL(ptext,prend,nchars)	do {\
int n = (nchars);\
if (n > 0) {\
rend_t * p = prend;\
memset ((ptext), SPACE_CHAR, (n * sizeof(text_t)));\
while (n-- > 0) *p++ = FILL_STYLE;\
}} while (0)
/*}}} */
/*{{{ screen_t */
/*
 * 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.nscrolled'
 *      how many lines have been scrolled (saved)
 *              0 <= TermWin.nscrolled <= TermWin.saveLines
 *
 * `TermWin.view_start'
 *      the offset back into the scrollback buffer for our current view
 *              -(TermWin.nscrolled) <= TermWin.view_start <= 0
 *
 * The selection region is defined for [0 .. (TermWin.nrow-1)], which
 * corresponds to the regular screen and for [-1 .. -(TermWin.nscrolled)]
 * which corresponds to the scrolled region.
 */

typedef struct
  {
    text_t *text;		/* all the text, including scrollback   */
    rend_t *rend;		/* rendition, using the `RS_' flags     */
    short row, col;		/* cursor position                      */
    short tscroll, bscroll;	/* top/bottom of scroll region          */
    short charset;		/* character set number [0..3]          */
    unsigned int flags;
  }
screen_t;
#define Screen_Relative		(1<<0)	/* relative origin mode flag      */
#define Screen_VisibleCursor	(1<<1)	/* cursor visible?                */
#define Screen_Autowrap		(1<<2)	/* auto-wrap flag         */
#define Screen_Insert		(1<<3)	/* insert mode (vs. overstrike)   */
#define Screen_WrapNext		(1<<4)	/* need to wrap for next char?    */

#define Screen_DefaultFlags	(Screen_VisibleCursor|Screen_Autowrap)

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

#ifdef NO_SECONDARY_SCREEN
#define NSCREENS	0
#else
#define NSCREENS	1
static screen_t swap_screen =
{NULL, NULL, 0, 0, 0, 0, 0, Screen_DefaultFlags};
#endif
/*}}} */
/*{{{ local variables */
static short current_screen = PRIMARY;
static short rvideo = 0;	/* reverse video */

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

#ifdef KANJI
static short multiByte = 0;
#endif

/* Data for save-screen */
static struct
  {
    short row, col;
    short charset;
    char charset_char;
    rend_t rstyle;
  }
save =
{
  0, 0, 0, 'B', DEFAULT_RSTYLE
};

/* 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 */
/*}}} */
/*{{{ selection */
/* 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 */
    short op;			/* current operation */
#define SELECTION_CLEAR	0
#define SELECTION_BEGIN	1
#define SELECTION_INIT	2
#define SELECTION_CONT	3
#define SELECTION_DONE	4
    short screen;		/* which screen is being used */
    struct
      {
	short 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);
/*----------------------------------------------------------------------*/
/*}}} */

/*----------------------------------------------------------------------*/
/*{{{ check_text() - check integrity of 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 = str;
      unsigned char *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 && *str)
    {
      if (!strcmp (str, "sjis"))
	kanji_decode = sjis2jis;
      else if (!strcmp (str, "eucj"))
	kanji_decode = eucj2jis;
    }
}
#endif /* KANJI */
/*}}} */

/*{{{ blank_lines() - add COUNT blank lines in the fill 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 short prev_nrow = -1, prev_ncol = -1;
  int i, ncol = 0;

  TermWin.view_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.nscrolled = 0;

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

      /* allocate space for scrollback (primary screen) + screen */
#if NSCREENS
      if (i)
	{
	  histsize = 0;
	  scr = &swap_screen;
	}
#endif

      /* 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.nscrolled)	/* enough */
		    {
		      scr->row += (n);
		      oldr = oldr_max;
		    }
		  else
		    {
		      scr->row += (TermWin.nscrolled);
		      oldr -= (TermWin.nscrolled);
		    }
		  TermWin.nscrolled -= (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.nscrolled += (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.nscrolled < 0)
    TermWin.nscrolled = 0;
  else if (TermWin.nscrolled > TermWin.saveLines)
    TermWin.nscrolled = 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
  tt_resize ();
}
/*}}} */
/*{{{ Restore power-on configuration */
/*
 * Clears screen, restores default fonts, etc
 *
 * also reset the scrollback buffer
 */
void
scr_poweron (void)
{
  screen_t *scr = &screen;
  int i;

  TermWin.view_start = 0;
  TermWin.nscrolled = 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; i <= NSCREENS; i++)
    {
      scr->tscroll = 0;
      scr->bscroll = (TermWin.nrow - 1);
      scr->row = scr->col = 0;
      scr->charset = 0;
      scr->flags = Screen_DefaultFlags;
#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)
{
  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 = rstyle;
      save.charset = screen.charset;
      save.charset_char = charsets[save.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
}
/*}}} */

/*{{{ Change between the main and alternate screen */
int
scr_change_screen (int scrn)
{
  register int x;
  TermWin.view_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++)
      {
	register text_t t;
	register 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.charset;
  screen.charset = swap_screen.charset;
  swap_screen.charset = x;

  x = screen.flags;
  screen.flags = swap_screen.flags;
  swap_screen.flags = x;

  screen.flags |= Screen_VisibleCursor;
  swap_screen.flags |= Screen_VisibleCursor;

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

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

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

  color = GET_FGCOLOR (rstyle);
#ifndef NO_BRIGHTCOLOR
  if (color >= minBright && color <= maxBright)
    {
      color -= (minBright - minColor);
      fprintf (stderr, "bright ");
    }
#endif
  fprintf (stderr, "%s on ", name[color]);

  color = GET_BGCOLOR (rstyle);
#ifndef NO_BRIGHTCOLOR
  if (color >= minBright && color <= maxBright)
    {
      color -= (minBright - minColor);
      fprintf (stderr, "bright ");
    }
#endif
  fprintf (stderr, "%s\n", name[color]);
}
#else
#define debug_colors()	((void)0)
#endif /* DEBUG_COLORS */
/*}}} */
/*{{{ Set foreground/background text color */
/*
 * Intensity:
 *      RS_Bold  = foreground
 *      RS_Blink = background
 */
void
scr_color (unsigned int color, unsigned int Intensity)
{
  switch (color)
    {
    case restoreFG:
      color = fgColor;
      assert (Intensity == RS_Bold);
      break;
    case restoreBG:
      color = bgColor;
      assert (Intensity == RS_Blink);
      break;

    default:
      if (Xdepth <= 2)		/* Monochrome - ignore color changes */
	{
	  switch (Intensity)
	    {
	    case RS_Bold:
	      color = fgColor;
	      break;
	    case RS_Blink:
	      color = bgColor;
	      break;
	    }
	}
#ifndef NO_BRIGHTCOLOR
      else
	{
	  if ((rstyle & Intensity) && color >= minColor && color <= maxColor)
	    color += (minBright - minColor);
	  else if (color >= minBright && color <= maxBright)
	    {
	      if (rstyle & Intensity)
		return;		/* already bold enough! */
	      color -= (minBright - minColor);
	    }
	}
#endif /* NO_BRIGHTCOLOR */
      break;
    }

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

#ifdef RXVT_GRAPHICS
/* return the current foreground/background colors */
inline int
scr_get_fgcolor (void)
{
  return GET_FGCOLOR (rstyle);
}
inline int
scr_get_bgcolor (void)
{
  return GET_BGCOLOR (rstyle);
}
#endif
/*}}} */
/*{{{ Change the rendition style */
void
scr_rendition (int set, int style)
{
  unsigned int color;

  debug_colors ();
  if (set)
    {
      rstyle |= style;		/* set rendition */
      switch (style)
	{
#ifndef NO_BRIGHTCOLOR
	case RS_Bold:
	  color = GET_FGCOLOR (rstyle);
	  scr_color ((color == fgColor ?
		      GET_FGCOLOR (colorfgbg) : color), RS_Bold);
	  break;

	case RS_Blink:
	  color = GET_BGCOLOR (rstyle);
	  scr_color ((color == bgColor ?
		      GET_BGCOLOR (colorfgbg) : color), RS_Blink);
	  break;
#endif /* NO_BRIGHTCOLOR */

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

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

#ifndef NO_BRIGHTCOLOR
	case RS_Bold:
	  color = GET_FGCOLOR (rstyle);
	  if (color >= minBright && color <= maxBright)
	    {
	      scr_color (color, RS_Bold);
	      /* scr_color (color - (minBright - minColor), RS_Bold); */
	      if ((rstyle & RS_fgMask) == (colorfgbg & RS_fgMask))
		scr_color (restoreFG, RS_Bold);
	    }
	  break;

	case RS_Blink:
	  color = GET_BGCOLOR (rstyle);
	  if (color >= minBright && color <= maxBright)
	    {
	      scr_color (color, RS_Blink);
	      /* scr_color (color - (minBright - minColor), RS_Blink); */
	      if ((rstyle & RS_bgMask) == (colorfgbg & RS_bgMask))
		scr_color (restoreBG, RS_Blink);
	    }
	  break;
#endif /* NO_BRIGHTCOLOR */

	case RS_RVid:
	  if (rvideo)
	    rstyle |= RS_RVid;	/* reverse video mode */
	  break;
	}
    }
  debug_colors ();
}
/*}}} */

/*{{{ Add lines */
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.view_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 (Gr_Displayed ())
	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':
		screen.flags &= ~Screen_WrapNext;
		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.flags &= ~Screen_WrapNext;
		screen.text[roffset + (TermWin.ncol)] = '\0';
		continue;
		break;

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

      if (screen.flags & 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.flags &= ~Screen_WrapNext;
	}
      if (screen.flags & 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--;
	  if (screen.flags & Screen_Autowrap)
	    screen.flags |= Screen_WrapNext;
	  else
	    screen.flags &= ~Screen_WrapNext;
	}
    }
}
/*}}} */

/*{{{ Scroll 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.nscrolled += count;
	  if (TermWin.nscrolled > TermWin.saveLines)
	    TermWin.nscrolled = 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;
	}
#if 0				/* this destroys the '\n' mark for the autowrapped lines */
      *(t_dst - 1) = '\0';	/* terminate previous line */
#endif
      /* 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 (Gr_Displayed ())
    Gr_scroll (count);
  return count;
}
/*}}} */

/*{{{ 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.flags & Screen_WrapNext)
    {
      int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol + 1);
      screen.text[roffset + (TermWin.ncol)] = '\0';
      screen.flags &= ~Screen_WrapNext;
    }
  else
    scr_gotorc (0, -1, RELATIVE);
}
/*}}} */

/*{{{ 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.view_start = 0;
  if (Gr_Displayed ())
    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.flags & Screen_WrapNext)
    {
      int roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol + 1);
      screen.text[roffset + (TermWin.ncol)] = '\0';
      screen.flags &= ~Screen_WrapNext;
    }

  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.flags & 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.view_start = 0;
  if (Gr_Displayed ())
    Gr_scroll (0);

  check_text ("index");

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

  if ((screen.row == screen.bscroll && dirn == UP) ||
      (screen.row == screen.tscroll && dirn == DN))
    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 count, roffset = (screen.row + TermWin.saveLines) * (TermWin.ncol + 1);
  TermWin.view_start = 0;
  if (Gr_Displayed ())
    Gr_scroll (0);

  if (screen.flags & Screen_WrapNext)
    {
      screen.text[roffset + (TermWin.ncol)] = '\0';
      screen.flags &= ~Screen_WrapNext;
    }
  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 count, roffset = (TermWin.saveLines) * (TermWin.ncol + 1);
  TermWin.view_start = 0;

  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 = (TermWin.saveLines) * (TermWin.ncol + 1);

  check_text ("E");
  TermWin.view_start = 0;
  memset (&screen.text[roffset], 'E',
	  ((TermWin.nrow) * (TermWin.ncol + 1)) * sizeof (text_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.view_start = 0;
  if (Gr_Displayed ())
    Gr_scroll (0);

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

  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 = (screen.row + TermWin.saveLines) * (TermWin.ncol + 1);
  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.view_start = 0;
  if (Gr_Displayed ())
    Gr_scroll (0);
  if (selection.op)
    selection_check ();

  screen.text[roffset + (TermWin.ncol)] = '\0';
  screen.flags &= ~Screen_WrapNext;

  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 = FILL_STYLE;
	}
    }
  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 = FILL_STYLE;
	}
    }
}
/*}}} */

/*{{{ 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 visible/invisible cursor */
void
scr_cursor_visible (int mode)
{
  if (mode)
    screen.flags |= Screen_VisibleCursor;
  else
    screen.flags &= ~Screen_VisibleCursor;
}
/*}}} */
/*{{{ Set/Unset automatic wrapping */
void
scr_autowrap (int mode)
{
  if (mode)
    screen.flags |= Screen_Autowrap;
  else
    screen.flags &= ~Screen_Autowrap;
}
/*}}} */

/*{{{ 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)
{
  if (mode)
    screen.flags |= Screen_Relative;
  else
    screen.flags &= ~Screen_Relative;
  scr_gotorc (0, 0, 0);
}
/*}}} */

/*{{{ Set/Unset automatic insert mode */
void
scr_insert_mode (int mode)
{
  if (mode)
    screen.flags |= Screen_Insert;
  else
    screen.flags &= ~Screen_Insert;
}
/*}}} */

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

  TermWin.view_start = ((len - y) * ((TermWin.nrow - 1) + TermWin.nscrolled)
			/ (len)) - (TermWin.nrow - 1);

  if (TermWin.view_start < 0)
    TermWin.view_start = 0;
  else if (TermWin.view_start > TermWin.nscrolled)
    TermWin.view_start = TermWin.nscrolled;

  if (Gr_Displayed ())
    Gr_scroll (0);

  return (TermWin.view_start - start);
}
/*}}} */

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

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

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

  if (TermWin.view_start < 0)
    TermWin.view_start = 0;
  else if (TermWin.view_start > TermWin.nscrolled)
    TermWin.view_start = TermWin.nscrolled;

  if (Gr_Displayed ())
    Gr_scroll (0);

  return (TermWin.view_start - start);
}
/*}}} */

/*{{{ selection service functions */
/*
 * 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.nscrolled) ||
      (selection.mark.row >= TermWin.nrow) ||
      (selection.beg.row < -TermWin.nscrolled) ||
      (selection.beg.row >= TermWin.nrow) ||
      (selection.end.row < -TermWin.nscrolled) ||
      (selection.end.row >= TermWin.nrow))
    {
      selection_reset ();
      return;
    }

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

  /* selection.mark.row > screen.row - TermWin.view_start
   * or
   * selection.end.row > screen.row - TermWin.view_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
static inline 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 selection */
/*
 * make the selection currently delimited by the selection end markers
 */
void
selection_make (Time tm)
{
  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.nscrolled || 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 c;
      int c1 = (r == startr ? startc : 0);
      int 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 (c = c1; c <= c2; c++)
	*str++ = screen.text[roffset + c];

      /*
       * end-of-line and not autowrap
       * remove trailing space, but don't remove an entire line!
       */
      if (c2 == (TermWin.ncol - 1) && !screen.text[roffset + (TermWin.ncol)])
	{
	  str--;
	  for (c = c2; c >= c1 && isspace (*str); c--)
	    str--;
	  str++;
	  *str++ = '\n';
	}
    }
  *str = '\0';

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

  /* Place in CUT_BUFFER0 for backup */
  XChangeProperty (Xdisplay, Xroot, 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 ev;
  static Atom xa_targets = None;
  if (xa_targets == None)
    xa_targets = XInternAtom (Xdisplay, "TARGETS", False);

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

  if (rq->target == xa_targets)
    {
      /*
       * On some systems, the Atom typedef is 64 bits wide.
       * We need to have a typedef that is exactly 32 bits wide,
       * because a format of 64 is not allowed by the X11 protocol.
       */
      typedef CARD32 Atom32;

      Atom32 target_list[2];

      target_list[0] = (Atom32) xa_targets;
      target_list[1] = (Atom32) XA_STRING;

      XChangeProperty (Xdisplay, rq->requestor, rq->property,
		   xa_targets, 8 * sizeof (target_list[0]), PropModeReplace,
		       (char *) target_list,
		       sizeof (target_list) / sizeof (target_list[0]));
      ev.xselection.property = rq->property;
    }
  else if (rq->target == XA_STRING)
    {
      XChangeProperty (Xdisplay, rq->requestor, rq->property,
		       XA_STRING, 8, PropModeReplace,
		       selection.text, selection.len);
      ev.xselection.property = rq->property;
    }
  XSendEvent (Xdisplay, rq->requestor, False, 0, &ev);
}
/*}}} */

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

  for (nitems = 0; p < pmax; p++)
    {
      /* do newline -> carriage-return mapping */
      if (*p == '\n')
	{
	  unsigned char cr = '\r';
	  tt_write (data, nitems);
	  tt_write (&cr, 1);
	  data += (nitems + 1);
	  nitems = 0;
	}
      else
	nitems++;
    }
  if (nitems)
    tt_write (data, nitems);
}

/*
 * Respond to a notification that a primary selection has been sent
 */
void
selection_paste (Window win, unsigned 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 tm, int x, int y)
{
  /* is release within the window? */
  if (x < 0 || y < 0 || x >= TermWin.width || y >= TermWin.height)
    return;

  if (selection.text != NULL)
    {
      /* internal selection */
      PasteIt (selection.text, selection.len);
    }
  else if (XGetSelectionOwner (Xdisplay, XA_PRIMARY) == None)
    {
      /* no primary selection - use CUT_BUFFER0 */
      selection_paste (Xroot, XA_CUT_BUFFER0, False);
    }
  else
    {
      Atom prop = XInternAtom (Xdisplay, "VT_SELECTION", False);
      XConvertSelection (Xdisplay, XA_PRIMARY, XA_STRING,
			 prop, TermWin.vt, tm);
    }
}
/*}}} */

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

  selection.op = SELECTION_CLEAR;
  selection.end.row = selection.mark.row = 0;
  selection.end.col = selection.mark.col = 0;

  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 + TermWin.saveLines) * (TermWin.ncol + 1));

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

  for (r = startr; r <= endr; r++)
    {
      int c1 = (r == startr ? startc : 0);
      int 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 at the specified col/row */
static void
selection_start_colrow (int col, int row)
{
  if (selection.op)
    {
      /* startr <= endr */
      if ((selection.end.row < -TermWin.nscrolled) ||
	  (selection.mark.row < -TermWin.nscrolled))
	{
	  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 = col;
  selection.beg.row = row;
  selection.beg.row -= TermWin.view_start;
}

/*
 * start a selection at the specified x/y pixel location
 */
void
selection_start (int x, int y)
{
  selection_start_colrow (Pixel2Col (x), Pixel2Row (y));
}
/*}}} */

/*{{{ extend the selection to the specified col/row */
static void
selection_extend_colrow (int col, int row)
{
  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_colrow (col, row);
      /*drop */
    default:
      return;
      break;
    }

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

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

  /* Figure out where new selection is */
  selection.end.col = col;
  selection.end.row = row;

  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.view_start;
  if (selection.end.row < -TermWin.nscrolled)
    {
      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 (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 : DN);

  /* 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 : DN);

  /* 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);
	    }
	}
    }
}

/*
 * extend the selection to the specified x/y pixel location
 */
void
selection_extend (int x, int y)
{
  selection_extend_colrow (Pixel2Col (x), Pixel2Row (y));
}
/*}}} */

/*{{{ double/triple click selection */
/*
 * by Edward. Der-Hua Liu, Taiwan
 * cut char support added by A. Haritsis <ah@doc.ic.ac.uk>
 */
void
selection_click (int clicks, int x, int y)
{
  if (clicks <= 1)
    {
      selection_start (x, y);	/* single click */
    }
  else
    {
      int beg_c, end_c, beg_r, end_r;
      text_t *text;

      /* ensure rows/columns are on the screen */
      x = Pixel2Col (x);
      x = (x <= 0 ? 0 : (x >= TermWin.ncol ? (TermWin.ncol - 1) : x));
      beg_c = end_c = x;

      y = Pixel2Row (y);
      y = (y <= 0 ? 0 : (y >= TermWin.nrow ? (TermWin.nrow - 1) : y));
      beg_r = end_r = y;

      switch (clicks)
	{
	case 3:
	  /*
	   * triple click
	   */
	  beg_c = 0;
	  end_c = (TermWin.ncol - 1);
	  break;

	case 2:
	  /*
	   * double click: handle autowrapped lines
	   */
	  for (text = (screen.text +
		       (beg_r + TermWin.saveLines - TermWin.view_start) *
		       (TermWin.ncol + 1));
	  /*forever */ ;
	       beg_r--, text -= (TermWin.ncol + 1))
	    {
	      while (beg_c > 0 &&
		     !strchr (rs_cutchars, text[beg_c - 1]))
		beg_c--;

	      if (beg_c == 0 &&
		  beg_r > (TermWin.view_start - TermWin.nscrolled) &&
		  *(text - 1) == '\n' &&
		  !strchr (rs_cutchars, *(text - 2)))
		beg_c = (TermWin.ncol - 1);
	      else
		break;
	    }

	  for (text = (screen.text +
		       (end_r + TermWin.saveLines - TermWin.view_start) *
		       (TermWin.ncol + 1));
	  /*forever */ ;
	       end_r++, text += (TermWin.ncol + 1))
	    {
	      while (end_c < (TermWin.ncol - 1) &&
		     !strchr (rs_cutchars, text[end_c + 1]))
		end_c++;

	      if (end_c == (TermWin.ncol - 1) &&
		  end_r < (TermWin.view_start + TermWin.nrow - 1) &&
		  text[TermWin.ncol] == '\n' &&
		  !strchr (rs_cutchars, text[TermWin.ncol + 1]))
		end_c = 0;
	      else
		break;
	    }
	  break;

	default:
	  return;
	  break;
	}
      selection_start_colrow (beg_c, beg_r);
      selection_extend_colrow (end_c, end_r);
    }
}
/*}}} */

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

/*{{{ Charset/Font functions */
/*
 * 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 int ch)
{
#ifdef KANJI
  multiByte = (set < 0);
  set = abs (set);
#endif
  charsets[set] = (unsigned char) ch;
  set_font_style ();
}
/*}}} */

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

  if (drawn_text == NULL)
    return;

  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 + Pixel2Width (width);
  end_row = row + 1 + Pixel2Height (height);

  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));
    }
}

/* touch the entire screen */
void
scr_touch (void)
{
  scr_expose (0, 0, TermWin.width, TermWin.height);
}
/*}}} */

/*{{{ Refresh screen */
/*
 * 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 (int type)
{
#ifdef XPM_BACKGROUND
#undef USE_XCOPYAREA
#ifndef NO_BOLDOVERSTRIKE
#define NO_BOLDOVERSTRIKE
#endif
#ifdef XPM_BUFFERING
  int update = 0;
#define drawBuffer	(TermWin.buf_pixmap)
#endif /* XPM_BUFFERING */
#endif /* XPM_BACKGROUND */

#ifndef drawBuffer
#define drawBuffer	(TermWin.vt)
#endif

  static int last_xcursor = 0;
  int r, roffset, doffset, xcursor;
#ifdef KANJI
  static int wbyte = 0;
#endif

  if (type == NO_REFRESH)	/* Window not visible, don't update */
    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.view_start);
  if (last_xcursor >= TermWin.nrow)
    {
      last_xcursor = 0;
    }
  else
    {
      last_xcursor *= (TermWin.ncol + 1);
      if (screen.flags & Screen_VisibleCursor)
	{
	  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 && !Gr_Displayed ())
    for (r = 0; r < TermWin.nrow; r++)
      {
	int k;
	int doffset = r * (TermWin.ncol + 1);
	int roffset = doffset + ((TermWin.saveLines - TermWin.view_start) *
				 (TermWin.ncol + 1));

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

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

	    roffset += (TermWin.ncol + 1);
	    doffset += (TermWin.ncol + 1);
	    r++;
	    for (count = 1;
		 ((r < TermWin.nrow) &&
		  !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),
		       Width2Pixel (1), Height2Pixel (count),
		       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 = 0 * (TermWin.ncol + 1);
  roffset = doffset + ((TermWin.saveLines - TermWin.view_start) * (TermWin.ncol + 1));

  /* For a first cut, do it one character at a time */
  for (r = 0;
       r < TermWin.nrow;
       roffset += (TermWin.ncol + 1),
       doffset += (TermWin.ncol + 1),
       r++)
    {
      int c;
      int ypixel = TermWin.font->ascent + Row2Pixel (r);

#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 count;
	  int x = roffset + c;
	  int 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 outlineCursor = False;	/* block cursor */
	      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--;
	      linebuf[count] = '\0';	/* zero-terminate */
#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)
			{
			  outlineCursor = True;
			  rend &= ~(RS_Cursor);
			}
		      else
			rvid = (!rvid
#ifndef NO_CURSORCOLOR
			   || (PixColors[cursorColor] != PixColors[bgColor])
#endif
			  );
		    }

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

		  /*
		   * 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;
		    }
		}

	      /* bold characters - order of preference:
	       * 1 - change the foreground color to colorBD
	       * 2 - change the foreground color to bright
	       * 3 - use boldFont
	       * 4 - simulate with overstrike
	       */
#ifdef NO_BRIGHTCOLOR
#define MonoBold(x)	((x) & (RS_Bold|RS_Blink))
#else /* NO_BRIGHTCOLOR */
#define MonoBold(x)	(((x) & RS_Bold) && fore == fgColor)
#endif /* NO_BRIGHTCOLOR */

/* # define MonoBold(x) ((((x) & RS_Bold) && fore == fgColor) || (((x) & RS_Blink) && back == bgColor)) */
/*
 * blink simulated by simulated bold (overstrike) seems a bit farfetched
 * comment this out (v2.17 - 04APR96) and see how many people complain
 */

	      if (fore != fgColor)
		{
		  gcvalue.foreground = PixColors[fore];
		  gcmask |= GCForeground;
		}
#ifndef NO_BOLDUNDERLINE
	      else
		{
		  if (rend & RS_Bold)	/* do bold first */
		    {
		      gcvalue.foreground = PixColors[colorBD];
		      if (gcvalue.foreground != PixColors[fgColor])
			{
			  gcmask |= GCForeground;
			  rend &= ~RS_Bold;
			}
		    }
		  else if (rend & RS_Uline)
		    {
		      gcvalue.foreground = PixColors[colorUL];
		      if (gcvalue.foreground != PixColors[fgColor])
			{
			  gcmask |= GCForeground;
			  rend &= ~RS_Uline;
			}
		    }
		}
#endif /* NO_BOLDUNDERLINE */

	      if (back != bgColor)
		{
		  gcvalue.background = PixColors[back];
		  gcmask |= GCBackground;
		}

#ifndef NO_CURSORCOLOR
	      if ((rend & RS_Cursor) &&
		  (PixColors[cursorColor] != PixColors[bgColor]))
		{
		  gcvalue.background = PixColors[cursorColor];
		  gcmask |= GCBackground;
		  if (PixColors[cursorColor2] != PixColors[fgColor])
		    {
		      gcvalue.foreground = PixColors[cursorColor2];
		      gcmask |= GCForeground;
		    }
		}
#endif /* NO_CURSORCOLOR */

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

#ifdef XPM_BACKGROUND
#ifdef XPM_BUFFERING
	      update = 1;
#endif /* XPM_BUFFERING */
#endif /* XPM_BACKGROUND */

/*----------------------------------------------------------------------*/
/*
 * how to write the strings
 */
#undef drawStringPrep
#undef drawString

#ifdef XPM_BACKGROUND
#ifdef XPM_BUFFERING
#define	drawStringPrep()\
XCopyArea (Xdisplay, TermWin.pixmap, drawBuffer, TermWin.gc,\
	xpixel, (ypixel - TermWin.font->ascent),\
	Width2Pixel (count), Height2Pixel (1), \
	xpixel, (ypixel - TermWin.font->ascent))
#else /* XPM_BUFFERING */
#define	drawStringPrep()\
XClearArea (Xdisplay, drawBuffer, xpixel, \
	(ypixel + 2 - Height2Pixel (1)),\
	Width2Pixel (count), Height2Pixel (1), 0)
#endif /* XPM_BUFFERING */
#endif /* XPM_BACKGROUND */
#define drawString(strFunc)\
strFunc (Xdisplay, drawBuffer, TermWin.gc, xpixel, ypixel, linebuf, count)
/*----------------------------------------------------------------------*/

#ifdef KANJI
	      if (wbyte)
		{
		  count /= 2;
#ifdef XPM_BACKGROUND
		  if (back == bgColor)
		    {
		      drawStringPrep ();
		      drawString (XDrawString16);
		    }
		  else
#endif /* XPM_BACKGROUND */
		    drawString (XDrawImageString16);
#ifndef NO_BOLDOVERSTRIKE
		  if (MonoBold (rend))
		    {
		      xpixel++;
		      drawString (XDrawString16);
		      xpixel--;
		    }
#endif /* NO_BOLDOVERSTRIKE */
		}
	      else
#endif /* KANJI */

#ifndef NO_BOLDFONT
	      if (MonoBold (rend) && TermWin.boldFont != NULL)
		{
		  XSetFont (Xdisplay, TermWin.gc, TermWin.boldFont->fid);

#ifdef XPM_BACKGROUND
		  if (back == bgColor)
		    {
		      drawStringPrep ();
		      drawString (XDrawString);
		    }
		  else
#endif /* XPM_BACKGROUND */
		    drawString (XDrawImageString);
		  XSetFont (Xdisplay, TermWin.gc, TermWin.font->fid);
		}
	      else
#endif /* NO_BOLDFONT */
		{
#ifdef XPM_BACKGROUND
		  if (back == bgColor)
		    {
		      drawStringPrep ();
		      drawString (XDrawString);
		    }
		  else
#endif /* XPM_BACKGROUND */
		    drawString (XDrawImageString);

#ifndef NO_BOLDOVERSTRIKE
		  if (MonoBold (rend))
		    {
		      xpixel++;
		      drawString (XDrawString);
		      xpixel--;
		    }
#endif /* NO_BOLDOVERSTRIKE */
		}
#undef MonoBold

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

	      if (outlineCursor)
		{
#ifndef NO_CURSORCOLOR
		  if (PixColors[cursorColor] != PixColors[bgColor])
		    {
		      gcvalue.foreground = PixColors[cursorColor];
		      gcmask |= GCForeground;
		      XChangeGC (Xdisplay, TermWin.gc, gcmask, &gcvalue);
		    }
#endif /* NO_CURSORCOLOR */
		  XDrawRectangle (Xdisplay,
				  drawBuffer,
				  TermWin.gc,
				  xpixel,
				  (ypixel - TermWin.font->ascent),
				  Width2Pixel (1) - 1,
				  Height2Pixel (1) - 1);
		}
	      if (gcmask)
		{
		  /* restore normal colors */
		  gcvalue.foreground = PixColors[fgColor];
		  gcvalue.background = PixColors[bgColor];
		  XChangeGC (Xdisplay, TermWin.gc, gcmask, &gcvalue);
		}
	    }
	}
    }
  if (screen.flags & Screen_VisibleCursor)
    {
      screen.rend[xcursor] &= ~(RS_Cursor);
#ifdef KANJI
      screen.rend[xcursor + 1] &= ~(RS_Cursor);
#endif /* KANJI */
    }

#ifdef XPM_BUFFERING
  if (update)
    {
      XClearArea (Xdisplay,
		  TermWin.vt,
		  0, Row2Pixel (0),
		  TermWin_TotalWidth (),
		  TermWin_TotalHeight (),
		  0);
      XFlush (Xdisplay);
    }
#endif /* XPM_BUFFERING */

#undef drawStringPrep
#undef drawString
#undef drawBuffer
}
/*}}} */

/*{{{ Tabs: set/clear */
/*
 *  -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);
}
/*}}} */

/*{{{ Print-Pipe */
#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.view_start;

  text = &screen.text[((TermWin.saveLines - TermWin.view_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
/*}}} */

/*{{{ Debugging for selection */
/*
 * 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%.*s\\n\n", nitems, nitems, data);
	  data += (nitems + 1);
	  nitems = 0;
	}
      else if (*p == '\r')
	{
	  printf ("[%d chars]\n%.*s\\r\n", nitems, 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,
	  Width2Pixel (1), Height2Pixel (1),
	  screen.tscroll, screen.bscroll);

  printf ("%d lines scrollback, %d lines scrolled, start = %d\n",
	  TermWin.saveLines, TermWin.nscrolled, TermWin.view_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
/*}}} */

/*{{{ Mouse Reporting */
/* add the bits:
 * @ 1 - shift
 * @ 2 - meta
 * @ 4 - ctrl
 */
#define ButtonNumber(x) ((x) == AnyButton ? 3 : ((x) - Button1))
#define KeyState(x) ((((x)&(ShiftMask|ControlMask))+(((x)&Mod1Mask)?2:0))<<2)
void
mouse_report (XButtonEvent * ev)
{
  tt_printf ("\033[M%c%c%c",
	     (040 + ButtonNumber (ev->button) + KeyState (ev->state)),
	     (041 + Pixel2Col (ev->x)),
	     (041 + Pixel2Row (ev->y)));
}

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

  if (report)
    {
      /* If either coordinate is past the end of the line:
       * "ESC [ T CxCyCxCyCxCy"
       * The parameters are begx, begy, 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)
	    tt_printf ("\033[t");	/* start/end are valid locations */
	  else
	    tt_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)
	    tt_printf ("\033[t");	/* start/end are valid locations */
	  else
	    tt_printf ("\033[T%c%c%c%c",
		       selection.end.col + 1, selection.end.row + 1,
		       selection.beg.col + 1, selection.beg.row + 1)
	    }
	    tt_printf ("%c%c", Pixel2Col (x) + 1, Pixel2Row (y) + 1);
	}
      else
      {
	selection_start_colrow (x - 1, y - 1);
	top = firstrow;
	bot = lastrow;
      }
    }
#endif
/*}}} */
/*----------------------- end-of-file (C source) -----------------------*/
