/*
 *--------------------------------*-C-*---------------------------------
 * File:        screen.c
 *
 * Copyright (c) 1994 R. Nation <nation@rocket.sanders.lockheed.com>
 * Copyright (c) 1997 Mj Olesen <olesen@me.QueensU.CA>
 * Copyright (c) 1997 Guylem Aznar <guylhem@danmark.linux.eu.org>
 *
 *  This program is free software; you can redistribute it and/or                *  modify it under the terms of the GNU General Public License version          *  2 as published by the Free Software Foundation.                              *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of               *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *----------------------------------------------------------------------
 */

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

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

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


#ifndef NO_SECONDARY_SCREEN
/*{{{ 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;
}
/*}}} */
#endif

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

/*{{{ 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)
{
#ifdef DEBUG
	check_text("backspace");
#endif
	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>
 * multiclick selection & drag added by G. Aznar <guylhem@danmark.linux.eu.org>
 */
#ifdef OLD_SELECTION_CLICK
void selection_click(int selection_click_extend, int clicks, int x, int y)
{
	int col, row, startx, endx, tx;

	switch (clicks) {
	case 1:
		if (selection_click_extend == 0)
			selection_start(x, y);
		else
			selection_extend(x, y);
		break;
	case 2:
		col = (x - 2) / TermWin.fwidth;
		row = (y - 2) / TermWin.fheight;
		if (col >= TermWin.ncol)
			col = TermWin.ncol - 1;
		if (row >= TermWin.nrow)
			row = TermWin.nrow - 1;
		if (col < 0)
			col = 0;
		if (row < 0)
			row = 0;
		tx = (row + TermWin.saveLines - TermWin.view_start) * (TermWin.ncol + 1);
		startx = col;
		endx = col;
		while (startx > 0 && !strchr(rs_cutchars, screen.text[tx + startx - 1]))
			startx--;
		while (endx < TermWin.ncol - 1 &&
		       !strchr(rs_cutchars, screen.text[tx + endx + 1]))
			endx++;
		startx = startx * TermWin.fwidth + 2;
		endx = endx * TermWin.fwidth + 2;
#ifndef NO_DOUBLECLICK_EXTEND
		if (selection_click_extend == 0)
#endif
			selection_start(startx, y);
		selection_extend(endx, y);
		break;
	case 3:
		row = (y - 2) / TermWin.fheight;
		if (row >= TermWin.nrow)
			row = TermWin.nrow - 1;
		if (row < 0)
			row = 0;
#ifndef NO_DOUBLECLICK_EXTEND
		if (selection_click_extend == 0)
#endif
			selection_start(2, y);
		selection_extend((TermWin.ncol - 1) * TermWin.fwidth + 2, y);
		break;
	}
	return;
}
#else
void selection_click(int selection_click_extend, int clicks, int x, int y)
{
	if (clicks <= 1) {
		if (selection_click_extend == 0)
			selection_start(x, y);
		else
			selection_extend(x, y);
	} else {
		int beg_c, end_c, beg_r, end_r;		/* for selection */
		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;
		}

/* if extending a multiclick, dont start at current position !!! */

#ifndef NO_DOUBLECLICK_EXTEND
		if (selection_click_extend == 0)
#endif
			selection_start_colrow(beg_c, beg_r);
		selection_extend_colrow(end_c, end_r);

#ifdef DEBUG_SELECTION
		printf("begin col: %d     begin row: %d     end col : %d     end row :%d\n", beg_c, beg_r, end_c, end_r);
#endif

	}
}
#endif				/* OLD_SELECTION_CLICK */
/*}}} */

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