ftp.nice.ch/pub/next/unix/editor/vim-5.0f.s.tar.gz#/vim-5.0f/src/screen.c

This is screen.c in view mode; [Download] [Up]

/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * screen.c: code for displaying on the screen
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

/*
 * The attributes that are actually active for writing to the screen.
 */
static int		screen_attr = 0;

#ifdef RIGHTLEFT
static int		rightleft = 0;	/* set to 1 for right to left in screen_fill */
#endif

/*
 * Positioning the cursor is reduced by remembering the last position.
 * Mostly used by windgoto() and screen_char().
 */
static int		screen_cur_row, screen_cur_col;	/* last known cursor position */

/*
 * When highlighting matches for the last use search pattern, search_hl_prog
 * points to the regexp program for it, and search_hl_attr contains the
 * attributes to be used.
 */
vim_regexp		*search_hl_prog = NULL;
int				search_hl_attr;

/*
 * Flags for w_valid.
 * These are suppose to be used only in screen.c.  From other files, use the
 * functions that set or reset them.
 *
 * VALID_BOTLINE	VALID_BOTLINE_AP
 *     on				on				w_botline valid
 *     off				on				w_botline approximated
 *     off				off				w_botline not valid
 *     on				off				not possible
 */
#define VALID_WROW		0x01	/* w_wrow (window row) is valid */
#define VALID_WCOL		0x02	/* w_wcol (window col) is valid */
#define VALID_VIRTCOL	0x04	/* w_virtcol (file col) is valid */
#define VALID_CHEIGHT	0x08	/* w_cline_height is valid */
#define VALID_CROW		0x10	/* w_cline_row is valid */
#define VALID_BOTLINE	0x20	/* w_botine and w_empty_rows are valid */
#define VALID_BOTLINE_AP 0x40	/* w_botine is approximated */

/*
 * Buffer for one screen line.
 */
static char_u *current_LinePointer;

static void win_update __ARGS((WIN *wp));
static int win_line __ARGS((WIN *, linenr_t, int, int, int attr));
#ifdef RIGHTLEFT
static void screen_line __ARGS((int row, int endcol, int clear_rest, int rightleft));
#define SCREEN_LINE(r, e, c, rl)	screen_line((r), (e), (c), (rl))
#else
static void screen_line __ARGS((int row, int endcol, int clear_rest));
#define SCREEN_LINE(r, e, c, rl)	screen_line((r), (e), (c))
#endif
static void start_search_hl __ARGS((void));
static void screen_start_highlight __ARGS((int attr));
static void comp_botline __ARGS((void));
static void screen_char __ARGS((char_u *, int, int));
static void screenclear2 __ARGS((void));
static void lineclear __ARGS((char_u *p));
static void check_cursor_moved __ARGS((WIN *wp));
static void curs_rows __ARGS((int do_botline));
static void validate_virtcol_win __ARGS((WIN *wp));
static int screen_ins_lines __ARGS((int, int, int, int));
static int highlight_status __ARGS((int *attr));
static void intro_message __ARGS((void));

/*
 * update_screenline() - like update_screen() but only for cursor line
 *
 * Must only be called when something in the cursor line has changed (e.g.
 * character inserted or deleted).
 *
 * Check if the size of the cursor line has changed since the last screen
 * update.  If it did change, lines below the cursor will move up or down and
 * we need to call the routine update_screen() to examine the entire screen.
 */
	void
update_screenline()
{
	int 		row;
	int			old_cline_height;

	if (!screen_valid(TRUE))
		return;

	if (must_redraw)					/* must redraw whole screen */
	{
		update_screen(must_redraw);
		return;
	}

	if (!redrawing())
	{
		must_redraw = NOT_VALID;		/* remember to update later */
		return;
	}

	/*
	 * If the screen has scrolled, or some lines after the cursor line have
	 * been invalidated, call update_screen().
	 */
	if (curwin->w_lsize_valid <= curwin->w_cursor.lnum - curwin->w_topline ||
								curwin->w_lsize_lnum[0] != curwin->w_topline)
	{
		update_screen(VALID_TO_CURSCHAR);
		return;
	}

	/*
	 * Check if the cursor line is still at the same position.  Be aware of
	 * the cursor having moved around, w_cline_row may be invalid, use the
	 * values from w_lsize[] by calling curs_rows().
	 */
	check_cursor_moved(curwin);
	if (!(curwin->w_valid & VALID_CROW))
		curs_rows(FALSE);
	old_cline_height = curwin->w_cline_height;
	if (p_hls)
		start_search_hl();

	/*
	 * w_virtcol needs to be valid.
	 */
	validate_virtcol();
	cursor_off();
	row = win_line(curwin, curwin->w_cursor.lnum,
				curwin->w_cline_row, curwin->w_height, highlight_attr[HLF_V]);

	display_hint = HINT_NONE;
	if (search_hl_prog != NULL)
	{
		vim_free(search_hl_prog);
		search_hl_prog = NULL;
	}

	if (row == curwin->w_height + 1)	/* line too long for window */
	{
		if (curwin->w_topline < curwin->w_cursor.lnum)
		{
			/*
			 * Window needs to be scrolled up to show the cursor line.
			 * We know w_botline was valid before the change, so it should now
			 * be one less.  This removes the need to recompute w_botline in
			 * update_topline().
			 */
			--curwin->w_botline;
			curwin->w_valid |= VALID_BOTLINE_AP;
		}
		update_topline();
		update_screen(VALID_TO_CURSCHAR);
	}
	else if (!dollar_vcol)
	{
		/*
		 * If the cursor line changed size, delete or insert screen lines and
		 * redraw the rest of the window.
		 */
		if (old_cline_height != curwin->w_cline_height)
		{
			if (curwin->w_cline_height < old_cline_height)
				win_del_lines(curwin, row,
					  old_cline_height - curwin->w_cline_height, FALSE, TRUE);
			else
				win_ins_lines(curwin,
								 curwin->w_cline_row + curwin->w_cline_height,
					  curwin->w_cline_height - old_cline_height, FALSE, TRUE);
			update_screen(VALID_TO_CURSCHAR);
		}
#ifdef SYNTAX_HL
		/*
		 * If syntax lost its sync, have to redraw the following lines.
		 */
		else if (syntax_present(curbuf) && row < cmdline_row &&
							  syntax_check_changed(curwin->w_cursor.lnum + 1))
			update_screen(VALID_TO_CURSCHAR);
#endif
		else if (clear_cmdline || redraw_cmdline)
			showmode();				/* clear cmdline, show mode and ruler */
	}
}

/*
 * Redraw the screen later, with UpdateScreen(type).
 * Set must_redraw only of not already set to a higher value.
 * e.g. if must_redraw is CLEAR, type == NOT_VALID will do nothing.
 */
	void
redraw_later(type)
	int		type;
{
	if (must_redraw < type)
		must_redraw = type;
}

/*
 * Mark all windows to be udpated later.
 */
	void
redraw_all_later(type)
	int			type;
{
	WIN				*wp;

	for (wp = firstwin; wp; wp = wp->w_next)
		if (wp->w_redr_type < type)
			wp->w_redr_type = type;
	redraw_later(type);
}

/*
 * Mark all windows that are editing the current buffer to be udpated later.
 */
	void
redraw_curbuf_later(type)
	int			type;
{
	WIN				*wp;

	for (wp = firstwin; wp; wp = wp->w_next)
		if (wp->w_redr_type < type && wp->w_buffer == curbuf)
			wp->w_redr_type = type;
	redraw_later(type);
}

/*
 * update all windows that are editing the current buffer
 */
	void
update_curbuf(type)
	int			type;
{
	redraw_curbuf_later(type);
	update_screen(type);
}

/*
 * update_screen()
 *
 * Based on the current value of curwin->w_topline, transfer a screenfull
 * of stuff from Filemem to NextScreen, and update curwin->w_botline.
 */

	void
update_screen(type)
	int 			type;
{
	WIN				*wp;
	static int		did_intro = FALSE;

	if (!screen_valid(TRUE))
		return;

	dollar_vcol = 0;

	if (must_redraw)
	{
		if (type < must_redraw)		/* use maximal type */
			type = must_redraw;
		must_redraw = 0;
	}

	if (curwin->w_lsize_valid == 0 && type < NOT_VALID)
		type = NOT_VALID;

	if (!redrawing())
	{
		must_redraw = type;		/* remember type for next time */
		curwin->w_redr_type = type;
		curwin->w_lsize_valid = 0;		/* don't use w_lsize[] now */
		return;
	}

	/*
	 * if the screen was scrolled up when displaying a message, scroll it down
	 */
	if (msg_scrolled)
	{
		clear_cmdline = TRUE;
		if (msg_scrolled > Rows - 5)		/* clearing is faster */
			type = CLEAR;
		else if (type != CLEAR)
		{
			if (screen_ins_lines(0, 0, msg_scrolled, (int)Rows) == FAIL)
				type = CLEAR;
			win_rest_invalid(firstwin);		/* should do only first/last few */
		}
		msg_scrolled = 0;
		need_wait_return = FALSE;
	}

	/*
	 * reset cmdline_row now (may have been changed temporarily)
	 */
	compute_cmdrow();

	if (type == CLEAR)			/* first clear screen */
	{
		screenclear();			/* will reset clear_cmdline */
		type = NOT_VALID;
	}

	if (clear_cmdline)			/* first clear cmdline */
	{
		check_for_delay(FALSE);
		msg_row = cmdline_row;
		msg_col = 0;
		msg_clr_eos();			/* will reset clear_cmdline */
	}

	/*
	 * Only start redrawing if there is really something to do.
	 */
	if (!((type == VALID && curwin->w_topline == curwin->w_lsize_lnum[0]) ||
			(type == INVERTED &&
					curwin->w_old_cursor_lnum == curwin->w_cursor.lnum &&
					(curwin->w_valid & VALID_VIRTCOL) &&
					curwin->w_old_curswant == curwin->w_curswant)))
	{
		/*
		 * go from top to bottom through the windows, redrawing the ones that
		 * need it
		 */
		curwin->w_redr_type = type;
		cursor_off();
		if (p_hls)
			start_search_hl();
		for (wp = firstwin; wp; wp = wp->w_next)
		{
			if (wp->w_redr_type)
				win_update(wp);
			if (wp->w_redr_status)
				win_redr_status(wp);
		}
		if (search_hl_prog != NULL)
		{
			vim_free(search_hl_prog);
			search_hl_prog = NULL;
		}
	}
	if (redraw_cmdline)
		showmode();

	display_hint = HINT_NONE;

	/* May put up an introductory message when not editing a file */
	if (!did_intro && bufempty() && curbuf->b_fname == NULL)
		intro_message();
	did_intro = TRUE;
}

#ifdef USE_GUI
/*
 * Update a single window, its status line and maybe the command line msg.
 * Used for the GUI scrollbar.
 */
	void
updateWindow(wp)
	WIN		*wp;
{
	cursor_off();
	if (p_hls)
		start_search_hl();
	win_update(wp);
	if (wp->w_redr_status)
		win_redr_status(wp);
	if (redraw_cmdline)
		showmode();
	if (search_hl_prog != NULL)
	{
		vim_free(search_hl_prog);
		search_hl_prog = NULL;
	}
}
#endif

/*
 * Update all windows for the current buffer, except curwin.
 * Used after modifying text, to update the other windows on the same buffer.
 */
	void
update_other_win()
{
	WIN		*wp;
	int		first = TRUE;

	for (wp = firstwin; wp; wp = wp->w_next)
		if (wp != curwin && wp->w_buffer == curbuf)
		{
			if (first)
			{
				cursor_off();
				if (p_hls)
					start_search_hl();
				first = FALSE;
			}
			wp->w_redr_type = NOT_VALID;
			/*
			 * don't do the actual redraw if wait_return() has just been
			 * called and the user typed a ":"
			 */
			if (!skip_redraw)
				win_update(wp);
		}

	if (search_hl_prog != NULL)
	{
		vim_free(search_hl_prog);
		search_hl_prog = NULL;
	}
}

/*
 * Update a single window.
 *
 * This may cause the windows below it also to be redrawn.
 */
	static void
win_update(wp)
	WIN		*wp;
{
	int				type = wp->w_redr_type;
	int				row;
	int				endrow;
	linenr_t		lnum;
	linenr_t		lastline = 0;	/* only valid if endrow != Rows -1 */
	int				done;			/* if TRUE, we hit the end of the file */
	int				didline;		/* if TRUE, we finished the last line */
	int 			srow = 0;		/* starting row of the current line */
	int 			idx;
	int 			i;
	long 			j;
	int				attr;
	static int		recursive = FALSE;	/* being called recursively */
	int				old_botline = wp->w_botline;
	int				must_start_top = FALSE;	/* update must start at top row */
	int				must_end_bot = FALSE;	/* update must end at bottom row */

	if (type == NOT_VALID)
	{
		wp->w_redr_status = TRUE;
		wp->w_lsize_valid = 0;
	}

	idx = 0;
	row = 0;
	lnum = wp->w_topline;
	validate_virtcol_win(wp);

	/* The number of rows shown is w_height. */
	/* The default last row is the status/command line. */
	endrow = wp->w_height;

	/*
	 * If there are no changes on the screen, handle two special cases:
	 * 1: we are off the top of the screen by a few lines: scroll down
	 * 2: wp->w_topline is below wp->w_lsize_lnum[0]: may scroll up
	 */
	if (type == VALID || type == VALID_TO_CURSCHAR ||
			type == VALID_BEF_CURSCHAR || type == INVERTED)
	{
		if (wp->w_topline < wp->w_lsize_lnum[0])	/* may scroll down */
		{
			j = wp->w_lsize_lnum[0] - wp->w_topline;
			if (j < wp->w_height - 2)				/* not too far off */
			{
				lastline = wp->w_lsize_lnum[0] - 1;
				i = plines_m_win(wp, wp->w_topline, lastline);
				if (i < wp->w_height - 2)		/* less than a screen off */
				{
					/*
					 * Try to insert the correct number of lines.
					 * If not the last window, delete the lines at the bottom.
					 * win_ins_lines may fail.
					 */
					if (win_ins_lines(wp, 0, i, FALSE, wp == firstwin) == OK &&
													wp->w_lsize_valid)
					{
						must_start_top = TRUE;
						endrow = i;

						if ((wp->w_lsize_valid += j) > wp->w_height)
							wp->w_lsize_valid = wp->w_height;
						for (idx = wp->w_lsize_valid; idx - j >= 0; idx--)
						{
							wp->w_lsize_lnum[idx] = wp->w_lsize_lnum[idx - j];
							wp->w_lsize[idx] = wp->w_lsize[idx - j];
						}
						idx = 0;
					}
				}
				else if (lastwin == firstwin)
				{
					screenclear();	/* far off: clearing the screen is faster */
					must_start_top = TRUE;
					must_end_bot = TRUE;
				}
			}
			else if (lastwin == firstwin)
			{
				screenclear();		/* far off: clearing the screen is faster */
				must_start_top = TRUE;
				must_end_bot = TRUE;
			}
		}
		else							/* may scroll up */
		{
			j = -1;
						/* try to find wp->w_topline in wp->w_lsize_lnum[] */
			for (i = 0; i < wp->w_lsize_valid; i++)
			{
				if (wp->w_lsize_lnum[i] == wp->w_topline)
				{
					j = i;
					break;
				}
				row += wp->w_lsize[i];
			}
			if (j == -1)	/* wp->w_topline is not in wp->w_lsize_lnum */
			{
				row = 0;	/* start at the first row */
				if (lastwin == firstwin)
				{
					screenclear();	/* far off: clearing the screen is faster */
					must_start_top = TRUE;
					must_end_bot = TRUE;
				}
			}
			else
			{
				/*
				 * Try to delete the correct number of lines.
				 * wp->w_topline is at wp->w_lsize_lnum[i].
				 */
				if (row && win_del_lines(wp, 0, row, FALSE, wp == firstwin)
																		== OK)
					must_end_bot = TRUE;
				if ((row == 0 || must_end_bot) && wp->w_lsize_valid)
				{
					/*
					 * Skip the lines (below the deleted lines) that are still
					 * valid and don't need redrawing.  Copy their info
					 * upwards, to compensate for the deleted lines.  Leave
					 * row and lnum on the first line that needs redrawing.
					 */
					srow = row;
					row = 0;
					for (;;)
					{
						if ((type == VALID_TO_CURSCHAR &&
												 lnum == wp->w_cursor.lnum) ||
								(type == VALID_BEF_CURSCHAR &&
											   lnum == wp->w_cursor.lnum - 1))
							break;
						if (row + srow + (int)wp->w_lsize[j] > wp->w_height)
							break;
						wp->w_lsize[idx] = wp->w_lsize[j];
						wp->w_lsize_lnum[idx] = lnum++;

						row += wp->w_lsize[idx++];
						if ((int)++j >= wp->w_lsize_valid)
							break;
					}
					wp->w_lsize_valid = idx;
				}
				else
					row = 0;		/* update all lines */
			}
		}
		if (endrow == wp->w_height && idx == 0) 	/* no scrolling */
			wp->w_lsize_valid = 0;
	}

	done = didline = FALSE;

	if (VIsual_active)		/* check if we are updating the inverted part */
	{
		linenr_t	from, to;

		/*
		 * Find the line numbers that need to be updated: The lines between
		 * the old cursor position and the current cursor position.  Also
		 * check if the Visual position changed.
		 */
		if (wp->w_cursor.lnum < wp->w_old_cursor_lnum)
		{
			from = wp->w_cursor.lnum;
			to = wp->w_old_cursor_lnum;
		}
		else
		{
			from = wp->w_old_cursor_lnum;
			to = wp->w_cursor.lnum;
		}

		if (VIsual.lnum != wp->w_old_visual_lnum)
		{
			if (wp->w_old_visual_lnum < from)
				from = wp->w_old_visual_lnum;
			if (wp->w_old_visual_lnum > to)
				to = wp->w_old_visual_lnum;
			if (VIsual.lnum < from)
				from = VIsual.lnum;
			if (VIsual.lnum > to)
				to = VIsual.lnum;
		}

		/*
		 * If in block mode and changed column or wp->w_curswant: update all
		 * lines.
		 * First compute the actual start and end column.
		 */
		if (VIsual_mode == Ctrl('V'))
		{
			colnr_t		from1, from2, to1, to2;

			getvcol(wp, &VIsual, &from1, NULL, &to1);
			getvcol(wp, &wp->w_cursor, &from2, NULL, &to2);
			if (from2 < from1)
				from1 = from2;
			if (to2 > to1)
				to1 = to2;
			++to1;
			if (wp->w_curswant == MAXCOL)
				to1 = MAXCOL;

			if (from1 != wp->w_old_cursor_fcol || to1 != wp->w_old_cursor_lcol)
			{
				if (from > VIsual.lnum)
					from = VIsual.lnum;
				if (to < VIsual.lnum)
					to = VIsual.lnum;
			}
			wp->w_old_cursor_fcol = from1;
			wp->w_old_cursor_lcol = to1;
		}

		/*
		 * There is no need to update lines above the top of the window.
		 * If "must_start_top" is set, always start at w_topline.
		 */
		if (from < wp->w_topline || must_start_top)
			from = wp->w_topline;

		/*
		 * If we know the value of w_botline, use it to restrict the update to
		 * the lines that are visible in the window.
		 */
		if (wp->w_valid & VALID_BOTLINE)
		{
			if (from >= wp->w_botline)
				from = wp->w_botline - 1;
			if (to >= wp->w_botline)
				to = wp->w_botline - 1;
		}

		/*
		 * Find the minimal part to be updated.
		 *
		 * If "lnum" is past "from" already, start at w_topline (can
		 * happen when scrolling).
		 */
		if (lnum > from)
		{
			lnum = wp->w_topline;
			idx = 0;
			row = 0;
		}
		while (lnum < from && idx < wp->w_lsize_valid)		/* find start */
		{
			row += wp->w_lsize[idx++];
			++lnum;
		}
		if (!must_end_bot && !must_start_top)
		{
			srow = row;
			for (j = idx; j < wp->w_lsize_valid; ++j)		/* find end */
			{
				if (wp->w_lsize_lnum[j] == to + 1)
				{
					endrow = srow;
					break;
				}
				srow += wp->w_lsize[j];
			}
		}
		else
		{
			/* Redraw until the end, otherwise we could miss something when
			 * doing CTRL-U. */
			endrow = wp->w_height;
		}

		wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
		wp->w_old_visual_lnum = VIsual.lnum;
		wp->w_old_curswant = wp->w_curswant;
	}
	else
	{
		wp->w_old_cursor_lnum = 0;
		wp->w_old_visual_lnum = 0;
	}

	attr = highlight_attr[HLF_V];

	/*
	 * Update the screen rows from "row" to "endrow".
	 * Start at line "lnum" which is at wp->w_lsize_lnum[idx].
	 */
	for (;;)
	{
		if (row == endrow)
		{
			didline = TRUE;
			break;
		}

		if (lnum > wp->w_buffer->b_ml.ml_line_count)
		{
			done = TRUE;		/* hit the end of the file */
			break;
		}
		srow = row;
		row = win_line(wp, lnum, srow, endrow, attr);
		if (row > endrow)		/* past end of screen */
		{
			/* we may need the size of that too long line later on */
			wp->w_lsize[idx] = plines_win(wp, lnum);
			wp->w_lsize_lnum[idx++] = lnum;
			break;
		}

		wp->w_lsize[idx] = row - srow;
		wp->w_lsize_lnum[idx++] = lnum;
		if (++lnum > wp->w_buffer->b_ml.ml_line_count)
		{
			done = TRUE;
			break;
		}
	}
	if (idx > wp->w_lsize_valid)
		wp->w_lsize_valid = idx;

	/* Do we have to do off the top of the screen processing ? */
	if (endrow != wp->w_height)
	{
		row = 0;
		for (idx = 0; idx < wp->w_lsize_valid && row < wp->w_height; idx++)
			row += wp->w_lsize[idx];

		if (row < wp->w_height)
		{
			done = TRUE;
		}
		else if (row > wp->w_height)	/* Need to blank out the last line */
		{
			lnum = wp->w_lsize_lnum[idx - 1];
			srow = row - wp->w_lsize[idx - 1];
			didline = FALSE;
		}
		else
		{
			lnum = wp->w_lsize_lnum[idx - 1] + 1;
			didline = TRUE;
		}
	}

	wp->w_empty_rows = 0;
	/*
	 * If we didn't hit the end of the file, and we didn't finish the last
	 * line we were working on, then the line didn't fit.
	 */
	if (!done && !didline)
	{
		if (lnum == wp->w_topline)
		{
			/*
			 * Single line that does not fit!
			 * Fill last line with '@' characters.
			 */
			screen_fill(wp->w_winpos + wp->w_height - 1,
					wp->w_winpos + wp->w_height, 0, (int)Columns, '@', '@',
					highlight_attr[HLF_AT]);
			wp->w_botline = lnum + 1;
		}
		else
		{
			/*
			 * Clear the rest of the screen and mark the unused lines.
			 */
#ifdef RIGHTLEFT
			if (wp->w_p_rl)
				rightleft = 1;
#endif
			screen_fill(wp->w_winpos + srow,
					wp->w_winpos + wp->w_height, 0, (int)Columns, '@', ' ',
					highlight_attr[HLF_AT]);
#ifdef RIGHTLEFT
			rightleft = 0;
#endif
			wp->w_botline = lnum;
			wp->w_empty_rows = wp->w_height - srow;
		}
	}
	else
	{
		/* make sure the rest of the screen is blank */
		/* put '~'s on rows that aren't part of the file. */
#ifdef RIGHTLEFT
		if (wp->w_p_rl)
			rightleft = 1;
#endif
		screen_fill(wp->w_winpos + row,
					wp->w_winpos + wp->w_height, 0, (int)Columns, '~', ' ',
					highlight_attr[HLF_AT]);
#ifdef RIGHTLEFT
		rightleft = 0;
#endif
		wp->w_empty_rows = wp->w_height - row;

		if (done)				/* we hit the end of the file */
			wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1;
		else
			wp->w_botline = lnum;
	}

	/*
	 * There is a trick with w_botline.  If we invalidate it on each change
	 * that might modify it, this will cause a lot of expensive calls to
	 * plines() in update_topline() each time.  Therefore the value of
	 * w_botline is often approximated, and this value is used to compute the
	 * value of w_topline.  If the value of w_botline was wrong, check that
	 * the value of w_topline is correct (cursor is on the visible part of the
	 * text).  If it's not, we need to redraw again.  Mostly this just means
	 * scrolling up a few lines, so it doesn't look too bad.  Only do this for
	 * the current window (where changes are relevant).
	 */
	wp->w_valid |= VALID_BOTLINE;
	wp->w_redr_type = 0;
	if (wp == curwin && wp->w_botline != old_botline && !recursive)
	{
		recursive = TRUE;
		update_topline();		/* may invalidate w_botline again */
		if (must_redraw)
		{
			win_update(wp);
			must_redraw = 0;
		}
		recursive = FALSE;
	}
}

/*
 * Display line "lnum" of window 'wp' on the screen.
 * Start at row "startrow", stop when "endrow" is reached.
 * wp->w_virtcol needs to be valid.
 *
 * Return the number of last row the line occupies.
 */

	static int
win_line(wp, lnum, startrow, endrow, attr)
	WIN				*wp;
	linenr_t		lnum;
	int 			startrow;
	int 			endrow;
	int				attr;				/* attributes for area highlighting */
{
	char_u 			*screenp;
	int				c;
	int				col;				/* visual column on screen */
	long			vcol;				/* visual column for tabs */
#ifdef SYNTAX_HL
	int				rcol;				/* real column in the line */
#endif
	int				row;				/* row in the window, excl w_winpos */
	int				screen_row;			/* row on the screen, incl w_winpos */
	char_u			*ptr;
#ifdef SYNTAX_HL
	char_u			*line;
#endif
	char_u			extra[16];			/* "%ld" must fit in here */
	char_u			*p_extra;
	char_u			*showbreak = NULL;
	int 			n_extra;
	int				n_spaces = 0;

	int				fromcol, tocol;		/* start/end of inverting */
	int				noinvcur = FALSE;	/* don't invert the cursor */
	FPOS			*top, *bot;
	int				area_highlighting;	/* Visual or incsearch highlighting in
										   this line */
	int				area_attr = 0;		/* attributes desired by highlighting */
	int				search_attr = 0;	/* attributes sesired by 'searchhl' */
#ifdef SYNTAX_HL
	int				syntax_attr = 0;	/* attributes desired by syntax */
	int				has_syntax = FALSE;	/* this buffer has syntax highl. */
#endif
	int				has_syn_or_lbr;		/* has syntax or linebreak */
	int				char_attr;			/* attributes for next character */
	char_u			*search_hl_start = NULL;
	char_u			*search_hl_end = NULL;

	if (startrow > endrow)				/* past the end already! */
		return startrow;

	row = startrow;
	screen_row = row + wp->w_winpos;

	/*
	 * To speed up the loop below, set has_syn_or_lbr when there is linebreak
	 * and/or syntax processing to be done.
	 */
	has_syn_or_lbr = wp->w_p_lbr;
#ifdef SYNTAX_HL
	if (syntax_present(wp->w_buffer))
	{
		syntax_start(wp, lnum);
		has_syntax = TRUE;
		has_syn_or_lbr = TRUE;
	}
#endif
	col = 0;
	vcol = 0;
	fromcol = -10;
	tocol = MAXCOL;
	area_highlighting = FALSE;
	char_attr = 0;

	/*
	 * handle visual active in this window
	 */
	if (VIsual_active && wp->w_buffer == curwin->w_buffer)
	{
										/* Visual is after curwin->w_cursor */
		if (ltoreq(curwin->w_cursor, VIsual))
		{
			top = &curwin->w_cursor;
			bot = &VIsual;
		}
		else							/* Visual is before curwin->w_cursor */
		{
			top = &VIsual;
			bot = &curwin->w_cursor;
		}
		if (VIsual_mode == Ctrl('V'))	/* block mode */
		{
			if (lnum >= top->lnum && lnum <= bot->lnum)
			{
				fromcol = wp->w_old_cursor_fcol;
				tocol = wp->w_old_cursor_lcol;
			}
		}
		else							/* non-block mode */
		{
			if (lnum > top->lnum && lnum <= bot->lnum)
				fromcol = 0;
			else if (lnum == top->lnum)
				getvcol(wp, top, (colnr_t *)&fromcol, NULL, NULL);
			if (lnum == bot->lnum)
			{
				getvcol(wp, bot, NULL, NULL, (colnr_t *)&tocol);
				++tocol;
			}

			if (VIsual_mode == 'V')		/* linewise */
			{
				if (fromcol > 0)
					fromcol = 0;
				tocol = MAXCOL;
			}
		}
			/* if the cursor can't be switched off, don't invert the
			 * character where the cursor is */
#ifndef MSDOS
		if (!highlight_match && *T_VI == NUL &&
							lnum == curwin->w_cursor.lnum && wp == curwin)
			noinvcur = TRUE;
#endif

		if (tocol <= (int)wp->w_leftcol)	/* inverting is left of screen */
			fromcol = 0;
										/* start of invert is left of screen */
		else if (fromcol >= 0 && fromcol < (int)wp->w_leftcol)
			fromcol = wp->w_leftcol;

		/* if inverting in this line, can't optimize cursor positioning */
		if (fromcol >= 0)
			area_highlighting = TRUE;
	}

	/*
	 * handle incremental search position highlighting
	 */
	else if (highlight_match && wp == curwin && search_match_len)
	{
		if (lnum == curwin->w_cursor.lnum)
		{
			getvcol(curwin, &(curwin->w_cursor),
											(colnr_t *)&fromcol, NULL, NULL);
			curwin->w_cursor.col += search_match_len;
			getvcol(curwin, &(curwin->w_cursor),
											(colnr_t *)&tocol, NULL, NULL);
			curwin->w_cursor.col -= search_match_len;
			area_highlighting = TRUE;
			if (fromcol == tocol)		/* do at least one character */
				tocol = fromcol + 1;	/* happens when past end of line */
		}
	}

	ptr = ml_get_buf(wp->w_buffer, lnum, FALSE);
#ifdef SYNTAX_HL
	line = ptr;
	rcol = 0;
#endif

	/*
	 * Handle highlighting the last used search pattern.
	 */
	if (search_hl_prog != NULL)
	{
		if (vim_regexec(search_hl_prog, ptr, TRUE))
		{
			search_hl_start = search_hl_prog->startp[0];
			search_hl_end = search_hl_prog->endp[0];
			area_highlighting = TRUE;
		}
	}

	if (!wp->w_p_wrap)		/* advance to first character to be displayed */
	{
		while ((colnr_t)vcol < wp->w_leftcol && *ptr)
		{
			vcol += win_chartabsize(wp, *ptr++, (colnr_t)vcol);
#ifdef SYNTAX_HL
			++rcol;
#endif
		}
		if ((colnr_t)vcol > wp->w_leftcol)
		{
			n_spaces = vcol - wp->w_leftcol;	/* begin with some spaces */
			vcol = wp->w_leftcol;
		}
	}

	screenp = current_LinePointer;
#ifdef RIGHTLEFT
	if (wp->w_p_rl)
	{
		col = Columns - 1;					/* col follows screenp here */
		screenp += Columns - 1;
	}
#endif
	if (wp->w_p_nu)
	{
#ifdef RIGHTLEFT
        if (wp->w_p_rl)						/* reverse line numbers */
		{
			char_u *c1, *c2, t;

			sprintf((char *)extra, " %-7ld", (long)lnum);
			for (c1 = extra, c2 = extra + STRLEN(extra) - 1; c1 < c2;
																   c1++, c2--)
			{
				t = *c1;
				*c1 = *c2;
				*c2 = t;
			}
		}
		else
#endif
			sprintf((char *)extra, "%7ld ", (long)lnum);
		p_extra = extra;
		n_extra = 8;
		vcol -= 8;		/* so vcol is 0 when line number has been printed */
	}
	else
	{
		p_extra = NULL;
		n_extra = 0;
	}

	for (;;)
	{
		if (area_highlighting)	/* Visual or match highlighting in this line */
		{
			if (((vcol == fromcol && !(noinvcur &&
										   (colnr_t)vcol == wp->w_virtcol)) ||
					(noinvcur && (colnr_t)vcol == wp->w_virtcol + 1 &&
							vcol >= fromcol)) && vcol < tocol)
				area_attr = attr;					/* start highlighting */
			else if (area_attr && (vcol == tocol ||
								(noinvcur && (colnr_t)vcol == wp->w_virtcol)))
				area_attr = 0;						/* stop highlighting */

			/*
			 * Check for start/end of search pattern match.
			 * After end, check for start/end of next match.
			 * When another match, have to check for start again.
			 * Watch out for matching an empty string!
			 */
			if (!n_extra && !n_spaces)
			{
				for (;;)
				{
					if (ptr == search_hl_start)
						search_attr = search_hl_attr;
					if (ptr == search_hl_end)
					{
						search_attr = 0;
						if (vim_regexec(search_hl_prog, ptr, TRUE))
						{
							search_hl_start = search_hl_prog->startp[0];
							search_hl_end = search_hl_prog->endp[0];
							if (search_hl_start != search_hl_end)
								continue;
							++search_hl_end; /* try again after empty match */
						}
					}
					break;
				}
			}

			if (area_attr)
				char_attr = area_attr;
#ifdef SYNTAX_HL
			else if (!search_attr && has_syntax)
				char_attr = syntax_attr;
#endif
			else
				char_attr = search_attr;
		}

	/* Get the next character to put on the screen. */

		/*
		 * if 'showbreak' is set it contains the characters to put at the
		 * start of each broken line
		 */
		if (
#ifdef RIGHTLEFT
			(wp->w_p_rl ? col == -1 : col == Columns)
#else
			col == Columns
#endif
			&& (*ptr != NUL || (wp->w_p_list && n_extra == 0) ||
										(n_extra && *p_extra) || n_spaces) &&
											  vcol != 0 && STRLEN(p_sbr) != 0)
			showbreak = p_sbr;
		if (showbreak != NULL)
		{
			c = *showbreak++;
			if (*showbreak == NUL)
				showbreak = NULL;
		}
		/*
		 * The 'extra' array contains the extra stuff that is inserted to
		 * represent special characters (non-printable stuff).
		 */
		else if (n_extra)
		{
			c = *p_extra++;
			n_extra--;
		}
		else if (n_spaces)
		{
			c = ' ';
			n_spaces--;
		}
		else
		{
			c = *ptr++;

			if (has_syn_or_lbr)
			{
#ifdef SYNTAX_HL
				if (has_syntax)
				{
					syntax_attr = get_syntax_attr(rcol++, line);
					if (!area_attr && !search_attr)
						char_attr = syntax_attr;
				}
#endif
				/*
				 * Found last space before word: check for line break
				 */
				if (wp->w_p_lbr && isbreak(c) && !isbreak(*ptr) &&
																!wp->w_p_list)
				{
					n_spaces = win_lbr_chartabsize(wp, ptr - 1,
							(colnr_t)vcol, NULL) - 1;
					if (vim_iswhite(c))
						c = ' ';
				}
			}

			/*
			 * Handling of non-printable characters.
			 */
			if (!safe_isprintchar(c))
			{
				/*
				 * when getting a character from the file, we may have to turn
				 * it into something else on the way to putting it into
				 * 'NextScreen'.
				 */
				if (c == TAB && !wp->w_p_list)
				{
					/* tab amount depends on current column */
					n_spaces = (int)wp->w_buffer->b_p_ts -
									vcol % (int)wp->w_buffer->b_p_ts - 1;
					c = ' ';
				}
				else if (c == NUL && wp->w_p_list)
				{
					p_extra = (char_u *)"";
					n_extra = 1;
					c = '$';
					--ptr;			/* put it back at the NUL */
				}
				else if (c != NUL)
				{
					p_extra = transchar(c);
					n_extra = charsize(c) - 1;
					c = *p_extra++;
				}
			}
		}

		/*
		 * At end of the text line.
		 */
		if (c == NUL)
		{
			if (area_attr)
			{
				/* invert at least one char, used for Visual and empty line or
				 * highlight match at end of line. If it's beyond the last
				 * char on the screen, just overwrite that one (tricky!) */
				if (vcol == fromcol)
				{
#ifdef RIGHTLEFT
					if (wp->w_p_rl)
					{
						if (col < 0)
						{
							++screenp;
							++col;
						}
					}
					else
#endif
					{
						if (col >= Columns)
						{
							--screenp;
							--col;
						}
					}
					*screenp = ' ';
					*(screenp + Columns) = char_attr;
#ifdef RIGHTLEFT
					if (wp->w_p_rl)
					{
						--screenp;
						--col;
					}
					else
#endif
					{
						++screenp;
						++col;
					}
				}
			}

			SCREEN_LINE(screen_row, col, TRUE, wp->w_p_rl);
			row++;

			/*
			 * Update w_cline_height if we can (saves a call to plines()
			 * later).
			 */
			if (wp == curwin && lnum == curwin->w_cursor.lnum)
			{
				curwin->w_cline_row = startrow;
				curwin->w_cline_height = row - startrow;
				curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
			}

			break;
		}

		/*
		 * At end of screen line.
		 */
		if (
#ifdef RIGHTLEFT
			wp->w_p_rl ? (col < 0) :
#endif
									(col >= Columns)
													)
		{
			SCREEN_LINE(screen_row, col, TRUE, wp->w_p_rl);
			col = 0;
			++row;
			++screen_row;
			if (!wp->w_p_wrap)
				break;
			if (row == endrow)		/* line got too long for screen */
			{
				++row;
				break;
			}
			screenp = current_LinePointer;
#ifdef RIGHTLEFT
			if (wp->w_p_rl)
			{
				col = Columns - 1;		/* col is not used if breaking! */
				screenp += Columns - 1;
			}
#endif
		}

		/*
		 * Store the character.
		 */
		*screenp = c;
		*(screenp + Columns) = char_attr;

#ifdef RIGHTLEFT
		if (wp->w_p_rl)
		{
			--screenp;
			--col;
		}
		else
#endif
		{
			++screenp;
			++col;
		}
		++vcol;

		/* stop before '$' of change command */
		if (dollar_vcol && wp == curwin && vcol >= (long)wp->w_virtcol)
		{
			SCREEN_LINE(screen_row, col, FALSE, wp->w_p_rl);
			break;
		}
	}

	return (row);
}

/*
 * Move one "cooked" screen line to the screen, but only the characters that
 * have actually changed.  Handle insert/delete character.
 * 'endcol' gives the columns where valid characters are (wp->w_p_rl matters).
 * 'clear_rest' is TRUE if the rest of the line needs to be cleared.
 */
	static void
screen_line(row, endcol, clear_rest
#ifdef RIGHTLEFT
									, rightleft
#endif
												)
	int		row;
	int		endcol;
	int		clear_rest;
#ifdef RIGHTLEFT
	int		rightleft;
#endif
{
	char_u 			*screenp_from;
	char_u 			*screenp_to;
	int				col = 0;
	int				force = FALSE;		/* force update rest of the line */

	screenp_from = current_LinePointer;
	screenp_to = LinePointers[row];

#ifdef RIGHTLEFT
	if (rightleft)
		while (col <= endcol)
		{
			if (clear_rest && (*screenp_to != ' ' ||
												*(screenp_to + Columns) != 0))
				screen_char(screenp_to, row, col);
			++col;
			++screenp_to;
			++screenp_from;
		}

	while (wp->w_p_rl ? (col < Columns) : (col < endcol))
#else
	while (col < endcol)
#endif
	{
		if (force || *screenp_from != *screenp_to ||
				*(screenp_from + Columns) != *(screenp_to + Columns))
		{
#if 0	/* TODO */
			/*
			 * Special trick to make copy/paste of wrapped lines work with
			 * xterm/screen:
			 *   If the first column is to be written, write the preceding
			 *   char twice.  This will work with all terminal types
			 *   (regardless of the xn,am settings).
			 * Only do this on a fast tty.
			 */
			if (col == 0 && p_tf && row > startrow &&
					LinePointers[screen_row - 1][Columns - 1 + Columns] ==
						char_attr)
			{
				if (screen_cur_row != screen_row - 1 ||
													screen_cur_col != Columns)
					screen_char(LinePointers[screen_row - 1] + Columns - 1,
										  screen_row - 1, (int)(Columns - 1));
				screen_char(LinePointers[screen_row - 1] + Columns - 1,
												screen_row - 1, (int)Columns);
				screen_start();
			}
#endif

			/*
			 * Special handling when 'xs' termcap flag set (hpterm):
			 * Attributes for characters are stored at the position where the
			 * cursor is when writing the highlighting code.  The
			 * start-highlighting code must be written with the cursor on the
			 * first highlighted character.  The stop-highlighting code must
			 * be written with the cursor just after the last highlighted
			 * character.
			 * Overwriting a character doesn't remove it's highlighting.  Need
			 * to clear the rest of the line, and force redrawing it
			 * completely.
			 */
			if (p_wiv && !force && *(screenp_to + Columns) &&
						 *(screenp_from + Columns) != *(screenp_to + Columns))
			{
				/*
				 * Need to remove highlighting attributes here.
				 */
				windgoto(row, col);
				outstr(T_CE);			/* clear rest of this screen line */
				screen_start();			/* don't know where cursor is now */
				force = TRUE;			/* force redraw of rest of the line */

				/*
				 * If the previous character was highlighted, need to stop
				 * highlighting at this character.
				 */
				if (col > 0 && *(screenp_to + Columns - 1))
				{
					screen_attr = *(screenp_to + Columns - 1);
					term_windgoto(row, col);
					screen_stop_highlight();
				}
				else
					screen_attr = 0;		/* highlighting has stopped */
			}
			*screenp_to = *screenp_from;
			*(screenp_to + Columns) = *(screenp_from + Columns);
			screen_char(screenp_to, row, col);
		}
		else if (p_wiv && col > 0)
		{
			if (*(screenp_to + Columns) == *(screenp_to + Columns - 1))
			{
				/*
				 * Don't output stop-highlight when moving the cursor, it will
				 * stop the highlighting when it should continue.
				 */
				screen_attr = 0;
			}
			else if (screen_attr)
			{
				screen_stop_highlight();
			}
		}

		++screenp_to;
		++screenp_from;
		++col;
	}

	if (clear_rest
#ifdef RIGHTLEFT
					&& !wp->w_p_rl
#endif
								   )
	{
		/* blank out the rest of the line */
		while (col < Columns && *screenp_to == ' ' &&
				*(screenp_to + Columns) == 0)
		{
			++screenp_to;
			++col;
		}
		if (col < Columns)
			screen_fill(row, row + 1, col, (int)Columns, ' ', ' ', 0);
	}
}

/*
 * mark all status lines for redraw; used after first :cd
 */
	void
status_redraw_all()
{
	WIN		*wp;

	for (wp = firstwin; wp; wp = wp->w_next)
		wp->w_redr_status = TRUE;
	update_screen(NOT_VALID);
}

/*
 * Redraw the status line of window wp.
 *
 * If inversion is possible we use it. Else '=' characters are used.
 */
	void
win_redr_status(wp)
	WIN		*wp;
{
	int		row;
	char_u	*p;
	int		len;
	int		fillchar;
	int		attr;

	if (wp->w_status_height)					/* if there is a status line */
	{
		fillchar = highlight_status(&attr);

		p = wp->w_buffer->b_fname;
		if (p == NULL)
			STRCPY(NameBuff, "[No File]");
		else
		{
			home_replace(wp->w_buffer, p, NameBuff, MAXPATHL);
			trans_characters(NameBuff, MAXPATHL);
		}
		p = NameBuff;
		len = STRLEN(p);

		if (wp->w_buffer->b_help || wp->w_buffer->b_changed ||
														 wp->w_buffer->b_p_ro)
			*(p + len++) = ' ';
		if (wp->w_buffer->b_help)
		{
			STRCPY(p + len, "[help]");
			len += 6;
		}
		if (wp->w_buffer->b_changed)
		{
			STRCPY(p + len, "[+]");
			len += 3;
		}
		if (wp->w_buffer->b_p_ro)
		{
			STRCPY(p + len, "[RO]");
			len += 4;
		}

		if (len > ru_col - 1)
		{
			p += len - (ru_col - 1);
			*p = '<';
			len = ru_col - 1;
		}

		row = wp->w_winpos + wp->w_height;
		screen_puts(p, row, 0, attr);
		screen_fill(row, row + 1, len, ru_col, fillchar, fillchar, attr);

		win_redr_ruler(wp, TRUE);
	}
	else	/* no status line, can only be last window */
		redraw_cmdline = TRUE;
	wp->w_redr_status = FALSE;
}

/*
 * Output a single character directly to the screen and update NextScreen.
 */
	void
screen_putchar(c, row, col, attr)
	int		c;
	int		row, col;
	int		attr;
{
	char_u		buf[2];

	buf[0] = c;
	buf[1] = NUL;
	screen_puts(buf, row, col, attr);
}

/*
 * Put string '*text' on the screen at position 'row' and 'col', with
 * attributes 'attr', and update NextScreen.
 * Note: only outputs within one row, message is truncated at screen boundary!
 * Note: if NextScreen, row and/or col is invalid, nothing is done.
 */
	void
screen_puts(text, row, col, attr)
	char_u	*text;
	int		row;
	int		col;
	int		attr;
{
	char_u	*screenp;

	if (NextScreen != NULL && row < Rows)			/* safety check */
	{
		screenp = LinePointers[row] + col;
		while (*text && col < Columns)
		{
			if (*screenp != *text || *(screenp + Columns) != attr ||
																exmode_active)
			{
				*screenp = *text;
				*(screenp + Columns) = attr;
				screen_char(screenp, row, col);
			}
			++screenp;
			++col;
			++text;
		}
	}
}

/*
 * Prepare for 'searchhl' highlighting.
 */
	static void
start_search_hl()
{
	search_hl_prog = last_pat_prog();
	search_hl_attr = highlight_attr[HLF_L];
}

/*
 * Reset cursor position. Use whenever cursor was moved because of outputting
 * something directly to the screen (shell commands) or a terminal control
 * code.
 */
	void
screen_start()
{
	screen_cur_row = screen_cur_col = 9999;
}

/*
 * Note that the cursor has gone down to the next line, column 0.
 * Used for Ex mode.
 */
	void
screen_down()
{
	screen_cur_col = 0;
	if (screen_cur_row < Rows - 1)
		++screen_cur_row;
}

      static void
screen_start_highlight(attr)
      int		attr;
{
	screen_attr = attr;
	if (full_screen
#ifdef WIN32
					&& termcap_active
#endif
									   )
	{
#ifdef USE_GUI
		if (gui.in_use)
		{
			char		buf[20];

			sprintf(buf, "\033|%dh", attr);				/* internal GUI code */
			OUTSTR(buf);
		}
		else
#endif
		{
#ifdef SYNTAX_HL
			if (attr > HL_ALL)							/* special HL attr. */
			{
				struct attr_entry *aep = NULL;

				if (*T_CCO != NUL)
				{
					aep = syn_cterm_attr2entry(attr);
					if (aep != NULL)
					{
						if (aep->ae_u.cterm.fg_color)
							term_fg_color(aep->ae_u.cterm.fg_color - 1);
						if (aep->ae_u.cterm.bg_color)
							term_bg_color(aep->ae_u.cterm.bg_color - 1);
					}
				}
				else
				{
					aep = syn_term_attr2entry(attr);
					if (aep != NULL && aep->ae_u.term.start != NULL)
						outstr(aep->ae_u.term.start);
				}
				if (aep == NULL)			/* did ":syntax clear" */
					attr = 0;
				else
					attr = aep->ae_attr;
			}
#endif
			if ((attr & HL_BOLD) && T_MD != NULL)		/* bold */
				outstr(T_MD);
			if ((attr & HL_STANDOUT) && T_SO != NULL)	/* standout */
				outstr(T_SO);
			if ((attr & HL_UNDERLINE) && T_US != NULL)	/* underline */
				outstr(T_US);
			if ((attr & HL_ITALIC) && T_CZH != NULL)	/* italic */
				outstr(T_CZH);
			if ((attr & HL_INVERSE) && T_MR != NULL)	/* inverse (reverse) */
				outstr(T_MR);
		}
	}
}

      void
screen_stop_highlight()
{
	int		do_ME = FALSE;			/* output T_ME code */

	if (screen_attr
#ifdef WIN32
						&& termcap_active
#endif
										   )
	{
#ifdef USE_GUI
		if (gui.in_use)
		{
			char		buf[20];

			sprintf(buf, "\033|%dH", screen_attr);		/* internal GUI code */
			OUTSTR(buf);
		}
		else
#endif
		{
#ifdef SYNTAX_HL
			if (screen_attr > HL_ALL)					/* special HL attr. */
			{
				struct attr_entry *aep = NULL;

				if (*T_CCO != NUL)
				{
					/*
					 * Assume that t_me restores the original colors!
					 */
					aep = syn_cterm_attr2entry(screen_attr);
					if (aep != NULL && (aep->ae_u.cterm.fg_color ||
													aep->ae_u.cterm.bg_color))
						do_ME = TRUE;
				}
				else
				{
					aep = syn_term_attr2entry(screen_attr);
					if (aep != NULL && aep->ae_u.term.stop != NULL)
					{
						if (STRCMP(aep->ae_u.term.stop, T_ME) == 0)
							do_ME = TRUE;
						else
							outstr(aep->ae_u.term.stop);
					}
				}
				if (aep == NULL)			/* did ":syntax clear" */
					screen_attr = 0;
				else
					screen_attr = aep->ae_attr;
			}
#endif
			/*
			 * Often all ending-codes are equal to T_ME.  Avoid outputting the
			 * same sequence several times.
			 */
			if (screen_attr & HL_STANDOUT)
			{
				if (STRCMP(T_SE, T_ME) == 0)
					do_ME = TRUE;
				else
					outstr(T_SE);
			}
			if (screen_attr & HL_UNDERLINE)
			{
				if (STRCMP(T_UE, T_ME) == 0)
					do_ME = TRUE;
				else
					outstr(T_UE);
			}
			if (screen_attr & HL_ITALIC)
			{
				if (STRCMP(T_CZR, T_ME) == 0)
					do_ME = TRUE;
				else
					outstr(T_CZR);
			}
			if (do_ME || (screen_attr & HL_BOLD) || (screen_attr & HL_INVERSE))
				outstr(T_ME);
		}
	}
	screen_attr = 0;
}

/*
 * put character '*p' on the screen at position 'row' and 'col'
 */
	static void
screen_char(p, row, col)
	char_u	*p;
	int 	row;
	int 	col;
{
	/*
	 * Outputting the last character on the screen may scrollup the screen.
	 * Don't to it!
	 */
	if (col == Columns - 1 && row == Rows - 1)
		return;

	/*
	 * Stop highlighting first, so it's easier to move the cursor.
	 */
	if (screen_attr != *(p + Columns))
		screen_stop_highlight();

	windgoto(row, col);

	if (screen_attr != *(p + Columns))
		screen_start_highlight(*(p + Columns));

	outchar(*p);
	screen_cur_col++;
}

/*
 * Fill the screen from 'start_row' to 'end_row', from 'start_col' to 'end_col'
 * with character 'c1' in first column followed by 'c2' in the other columns.
 * Use attributes 'attr'.
 */
	void
screen_fill(start_row, end_row, start_col, end_col, c1, c2, attr)
	int 	start_row, end_row;
	int		start_col, end_col;
	int		c1, c2;
	int		attr;
{
	int				row;
	int				col;
	char_u			*screenp;
	char_u			*attrp;
	int				did_delete;
	int				c;

	if (end_row > Rows)				/* safety check */
		end_row = Rows;
	if (end_col > Columns)			/* safety check */
		end_col = Columns;
	if (NextScreen == NULL ||
			start_row >= end_row || start_col >= end_col)	/* nothing to do */
		return;

	for (row = start_row; row < end_row; ++row)
	{
			/* try to use delete-line termcap code */
		did_delete = FALSE;
		if (attr == 0 && c2 == ' ' && end_col == Columns && *T_CE != NUL
#ifdef RIGHTLEFT
					&& !rightleft
#endif
									)
		{
			/*
			 * check if we really need to clear something
			 */
			col = start_col;
			screenp = LinePointers[row] + start_col;
			if (c1 != ' ')						/* don't clear first char */
			{
				++col;
				++screenp;
			}

			/* skip blanks (used often, keep it fast!) */
			attrp = screenp + Columns;
			while (col < end_col && *screenp == ' ' && *attrp == 0)
			{
				++col;
				++screenp;
				++attrp;
			}
			if (col < end_col)			/* something to be cleared */
			{
				screen_stop_highlight();
				term_windgoto(row, col);/* clear rest of this screen line */
				outstr(T_CE);
				screen_start();			/* don't know where cursor is now */
				col = end_col - col;
				while (col--)			/* clear chars in NextScreen */
				{
					*attrp++ = 0;
					*screenp++ = ' ';
				}
			}
			did_delete = TRUE;			/* the chars are cleared now */
		}

		screenp = LinePointers[row] +
#ifdef RIGHTLEFT
			(rightleft ? (int)Columns - 1 - start_col : start_col);
#else
													start_col;
#endif
		c = c1;
		for (col = start_col; col < end_col; ++col)
		{
			if (*screenp != c || *(screenp + Columns) != attr)
			{
				*screenp = c;
				*(screenp + Columns) = attr;
				if (!did_delete || c != ' ')
					screen_char(screenp, row,
#ifdef RIGHTLEFT
							rightleft ? Columns - 1 - col :
#endif
															col);
			}
#ifdef RIGHTLEFT
			if (rightleft)
				--screenp;
			else
#endif
				++screenp;
			if (col == start_col)
			{
				if (did_delete)
					break;
				c = c2;
			}
		}
		if (row == Rows - 1)			/* overwritten the command line */
		{
			redraw_cmdline = TRUE;
			if (c1 == ' ' && c2 == ' ')
				clear_cmdline = FALSE;	/* command line has been cleared */
		}
	}
}

/*
 * compute wp->w_botline. Can be called after wp->w_topline changed.
 */
	static void
comp_botline()
{
	int			n;
	linenr_t	lnum;
	int			done;

	/*
	 * If w_cline_row is valid, start there.
	 * Otherwise have to start at w_topline.
	 */
	check_cursor_moved(curwin);
	if (curwin->w_valid & VALID_CROW)
	{
		lnum = curwin->w_cursor.lnum;
		done = curwin->w_cline_row;
	}
	else
	{
		lnum = curwin->w_topline;
		done = 0;
	}

	for ( ; lnum <= curwin->w_buffer->b_ml.ml_line_count; ++lnum)
	{
		n = plines(lnum);
		if (lnum == curwin->w_cursor.lnum)
		{
			curwin->w_cline_row = done;
			curwin->w_cline_height = n;
			curwin->w_valid |= (VALID_CROW|VALID_CHEIGHT);
		}
		if (done + n > curwin->w_height)
			break;
		done += n;
	}

	/* curwin->w_botline is the line that is just below the window */
	curwin->w_botline = lnum;
	curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;

	/*
	 * Also set curwin->w_empty_rows, otherwise scroll_cursor_bot() won't work
	 */
	if (done == 0)
		curwin->w_empty_rows = 0;		/* single line that doesn't fit */
	else
		curwin->w_empty_rows = curwin->w_height - done;
}

	void
screenalloc(clear)
	int		clear;
{
	int				new_row, old_row;
	WIN				*wp;
	int				outofmem = FALSE;
	int				len;
	char_u			*new_NextScreen;
	char_u			**new_LinePointers;
	static int		entered = FALSE;			/* avoid recursiveness */

	/*
	 * Allocation of the screen buffers is done only when the size changes
	 * and when Rows and Columns have been set and we have started doing full
	 * screen stuff.
	 */
	if ((NextScreen != NULL &&
						  Rows == screen_Rows && Columns == screen_Columns) ||
			Rows == 0 || Columns == 0 || (!full_screen && NextScreen == NULL))
		return;

	/*
	 * It's possible that we produce an out-of-memory message below, which
	 * will cause this function to be called again.  To break the loop, just
	 * return here.
	 */
	if (entered)
		return;
	entered = TRUE;

	comp_col();			/* recompute columns for shown command and ruler */

	/*
	 * We're changing the size of the screen.
	 * - Allocate new arrays for NextScreen.
	 * - Move lines from the old arrays into the new arrays, clear extra
	 *   lines (unless the screen is going to be cleared).
	 * - Free the old arrays.
	 *
	 * If anything fails, make NextScreen NULL, so we don't do anything!
	 * Continuing with the old NextScreen may result in a crash, because the
	 * size is wrong.
	 */
	for (wp = firstwin; wp; wp = wp->w_next)
		win_free_lsize(wp);

	new_NextScreen = (char_u *)malloc((size_t) ((Rows + 1) * Columns * 2));
	new_LinePointers = (char_u **)malloc(sizeof(char_u *) * Rows);

	for (wp = firstwin; wp; wp = wp->w_next)
	{
		if (win_alloc_lsize(wp) == FAIL)
		{
			outofmem = TRUE;
			break;
		}
	}

	if (new_NextScreen == NULL || new_LinePointers == NULL || outofmem)
	{
		do_outofmem_msg();
		vim_free(new_NextScreen);
		new_NextScreen = NULL;
		vim_free(new_LinePointers);
		new_LinePointers = NULL;
	}
	else
	{
		for (new_row = 0; new_row < Rows; ++new_row)
		{
			new_LinePointers[new_row] = new_NextScreen + new_row * Columns * 2;

			/*
			 * If the screen is not going to be cleared, copy as much as
			 * possible from the old screen to the new one and clear the rest
			 * (used when resizing the window at the "--more--" prompt or when
			 * executing an external command, for the GUI).
			 */
			if (!clear)
			{
				lineclear(new_LinePointers[new_row]);
				old_row = new_row + (screen_Rows - Rows);
				if (old_row >= 0)
				{
					if (screen_Columns < Columns)
						len = screen_Columns;
					else
						len = Columns;
					vim_memmove(new_LinePointers[new_row],
							LinePointers[old_row], (size_t)len);
					vim_memmove(new_LinePointers[new_row] + Columns,
							LinePointers[old_row] + screen_Columns,
																 (size_t)len);
				}
			}
		}
		current_LinePointer = new_NextScreen + Rows * Columns * 2;
	}

	vim_free(NextScreen);
	vim_free(LinePointers);
	NextScreen = new_NextScreen;
	LinePointers = new_LinePointers;

	must_redraw = CLEAR;		/* need to clear the screen later */
	if (clear)
		screenclear2();

#ifdef USE_GUI
	else if (gui.in_use && NextScreen != NULL && Rows != screen_Rows)
	{
		gui_redraw_block(0, 0, Rows - 1, Columns - 1);
		/*
		 * Adjust the position of the cursor, for when executing an external
		 * command.
		 */
		if (msg_row >= Rows)				/* Rows got smaller */
			msg_row = Rows - 1;				/* put cursor at last row */
		else if (Rows > screen_Rows)		/* Rows got bigger */
			msg_row += Rows - screen_Rows;	/* put cursor in same place */
		if (msg_col >= Columns)				/* Columns got smaller */
			msg_col = Columns - 1;			/* put cursor at last column */
	}
#endif

	screen_Rows = Rows;
	screen_Columns = Columns;
	entered = FALSE;
}

	void
screenclear()
{
	check_for_delay(FALSE);
	screenalloc(FALSE);		/* allocate screen buffers if size changed */
	screenclear2();			/* clear the screen */
}

	static void
screenclear2()
{
	int		i;

	if (starting || NextScreen == NULL)
		return;

	screen_stop_highlight();	/* don't want highlighting here */
	outstr(T_CL);				/* clear the display */

								/* blank out NextScreen */
	for (i = 0; i < Rows; ++i)
		lineclear(LinePointers[i]);

	screen_cleared = TRUE;			/* can use contents of NextScreen now */

	win_rest_invalid(firstwin);
	clear_cmdline = FALSE;
	redraw_cmdline = TRUE;
	if (must_redraw == CLEAR)		/* no need to clear again */
		must_redraw = NOT_VALID;
	compute_cmdrow();
	msg_pos((int)Rows - 1, 0);		/* put cursor on last line for messages */
	screen_start();					/* don't know where cursor is now */
	msg_scrolled = 0;				/* can't scroll back */
	msg_didany = FALSE;
	msg_didout = FALSE;
}

/*
 * Clear one line in NextScreen.
 */
	static void
lineclear(p)
	char_u	*p;
{
	(void)vim_memset(p, ' ', (size_t)Columns);
	(void)vim_memset(p + Columns, 0, (size_t)Columns);
}

/*
 * Update curwin->w_topline and redraw if necessary.
 */
	void
update_topline_redraw()
{
	update_topline();
	if (must_redraw)
		update_screen(must_redraw);
}

/*
 * Update curwin->w_topline to move the cursor onto the screen.
 */
	void
update_topline()
{
	long 			line_count;
	int 			temp;
	linenr_t		old_topline;

	if (!screen_valid(TRUE))
		return;

	old_topline = curwin->w_topline;

	/*
	 * If the buffer is empty, always set topline to 1.
	 */
	if (bufempty()) 			/* special case - file is empty */
	{
		if (curwin->w_topline != 1)
			redraw_later(NOT_VALID);
		curwin->w_topline = 1;
		curwin->w_botline = 2;
		curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
	}

	/*
	 * If the cursor is above the top of the window, scroll the window to put
	 * it at the top of the window.
	 * If we weren't very close to begin with, we scroll to put the cursor in
	 * the middle of the window.
	 */
	else if (curwin->w_cursor.lnum < curwin->w_topline + p_so &&
														curwin->w_topline > 1)
	{
		temp = curwin->w_height / 2 - 1;
		if (temp < 2)
			temp = 2;
								/* not very close, put cursor halfway screen */
		if (curwin->w_topline + p_so - curwin->w_cursor.lnum >= temp)
			scroll_cursor_halfway(FALSE);
		else
			scroll_cursor_top((int)p_sj, FALSE);
	}

	/*
	 * If the cursor is below the bottom of the window, scroll the window
	 * to put the cursor on the window. If the cursor is less than a
	 * windowheight down compute the number of lines at the top which have
	 * the same or more rows than the rows of the lines below the bottom.
	 * When w_botline is invalid, recompute it first, to avoid a redraw later.
	 * If w_botline was approximated, we might need a redraw later in a few
	 * cases, but we don't want to spend (a lot of) time recomputing w_botline
	 * for every small change.
	 */
	else
	{
		if (!(curwin->w_valid & VALID_BOTLINE_AP))
			validate_botline();
		if ((long)curwin->w_cursor.lnum >= (long)curwin->w_botline - p_so &&
				curwin->w_botline <= curbuf->b_ml.ml_line_count)
		{
			line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + p_so;
			if (line_count <= curwin->w_height + 1)
				scroll_cursor_bot((int)p_sj, FALSE);
			else
				scroll_cursor_halfway(FALSE);
		}
	}

	/*
	 * Need to redraw when topline changed.
	 */
	if (curwin->w_topline != old_topline)
		redraw_later(VALID);
}

	void
update_curswant()
{
	if (curwin->w_set_curswant)
	{
		validate_virtcol();
		curwin->w_curswant = curwin->w_virtcol;
		curwin->w_set_curswant = FALSE;
	}
}

	void
windgoto(row, col)
	int		row;
	int		col;
{
	char_u			*p;
	int				i;
	int				plan;
	int				cost;
	int				wouldbe_col;
	int				noinvcurs;
	char_u			*bs;
	int				goto_cost;
	int				attr;

#define GOTO_COST	7	/* asssume a term_windgoto() takes about 7 chars */
#define HIGHL_COST	5	/* assume unhighlight takes 5 chars */

#define PLAN_BS		1
#define PLAN_CR		2
#define PLAN_NL		3
#define PLAN_WRITE	4

	if (col != screen_cur_col || row != screen_cur_row)
	{
		/* check if no cursor movement is allowed in highlight mode */
		if (screen_attr && *T_MS == NUL)
			noinvcurs = HIGHL_COST;
		else
			noinvcurs = 0;
		goto_cost = GOTO_COST + noinvcurs;

		/*
		 * Plan how to do the positioning:
		 * 1. Use CR to move it to column 0, same row.
		 * 2. Use BS to move it a few columns to the left.
		 * 3. Use NL to move a few lines down, column 0.
		 * 4. Move a few columns to the right by writing characters.
		 *
		 * Don't do this if the cursor went beyond the last column, the cursor
		 * position is unknown then (some terminals wrap, some don't )
		 *
		 * First check if the highlighting attibutes allow us to write
		 * characters to move the cursor to the right.
		 */
		if (row >= screen_cur_row && screen_cur_col < Columns)
		{
			/*
			 * If the cursor is in the same row, bigger col, we can use CR
			 * or backspace.
			 */
			bs = NULL;						/* init for GCC */
			attr = screen_attr;
			if (row == screen_cur_row && col < screen_cur_col)
			{
				if (*T_BC)
					bs = T_BC;
				else
					bs = (char_u *)"\b";
				cost = (screen_cur_col - col) * STRLEN(bs);
				if (col + 1 < cost)			/* using CR is less characters */
				{
					plan = PLAN_CR;
					wouldbe_col = 0;
					cost = 1;				/* CR is just one character */
				}
				else
				{
					plan = PLAN_BS;
					wouldbe_col = col;
				}
				if (noinvcurs)				/* will stop highlighting */
				{
					cost += noinvcurs;
					attr = 0;
				}
			}

			/*
			 * If the cursor is above where we want to be, we can use CR LF.
			 */
			else if (row > screen_cur_row)
			{
				plan = PLAN_NL;
				wouldbe_col = 0;
				cost = (row - screen_cur_row) * 2;	/* CR LF */
				if (noinvcurs)				/* will stop highlighting */
				{
					cost += noinvcurs;
					attr = 0;
				}
			}

			/*
			 * If the cursor is in the same row, smaller col, just use write.
			 */
			else
			{
				plan = PLAN_WRITE;
				wouldbe_col = screen_cur_col;
				cost = 0;
			}

			/*
			 * Check if any characters that need to be written have the
			 * correct attributes.
			 */
			i = col - wouldbe_col;
			if (i > 0)
				cost += i;
			if (cost < goto_cost && i > 0)
			{
				/*
				 * Check if the attributes are correct without additionally
				 * stopping highlighting.
				 */
				p = LinePointers[row] + wouldbe_col + Columns;
				while (i && *p++ == attr)
					--i;
				if (i)
				{
					/*
					 * Try if it works when highlighting is stopped here.
					 */
					if (*--p == 0)
					{
						cost += noinvcurs;
						while (i && *p++ == 0)
							--i;
					}
					if (i)
						cost = 999;		/* different attributes, don't do it */
				}
			}

			/*
			 * We can do it without term_windgoto()!
			 */
			if (cost < goto_cost)
			{
				if (plan == PLAN_BS)
				{
					if (noinvcurs)
						screen_stop_highlight();
					while (screen_cur_col > col)
					{
						outstr(bs);
						--screen_cur_col;
					}
				}
				else if (plan == PLAN_CR)
				{
					if (noinvcurs)
						screen_stop_highlight();
					outchar('\r');
					screen_cur_col = 0;
				}
				else if (plan == PLAN_NL)
				{
					if (noinvcurs)
						screen_stop_highlight();
					while (screen_cur_row < row)
					{
						outchar('\n');
						++screen_cur_row;
					}
					screen_cur_col = 0;
				}
				i = col - screen_cur_col;
				if (i > 0)
				{
					p = LinePointers[row] + screen_cur_col;
					while (i)
					{
						if (*(p + Columns) != screen_attr)
							screen_stop_highlight();
						outchar(*p++);
						--i;
					}
				}
			}
		}
		else
			cost = 999;

		if (cost >= goto_cost)
		{
			if (noinvcurs)
				screen_stop_highlight();
			if (row == screen_cur_row && (col > screen_cur_col) &&
																*T_CRI != NUL)
				term_cursor_right(col - screen_cur_col);
			else
				term_windgoto(row, col);
		}
		screen_cur_row = row;
		screen_cur_col = col;
	}
}

/*
 * Set cursor to current position.
 */
	void
setcursor()
{
	if (redrawing())
	{
		validate_cursor();
		windgoto(curwin->w_winpos + curwin->w_wrow,
#ifdef RIGHTLEFT
				curwin->w_p_rl ? (int)Columns - 1 - curwin->w_wcol :
#endif
															curwin->w_wcol);
	}
}

/*
 * Recompute topline to put the cursor at the top of the window.
 * Scroll at least "min_scroll" lines.
 * If "always" is TRUE, always set topline (for "zt").
 */
	void
scroll_cursor_top(min_scroll, always)
	int		min_scroll;
	int		always;
{
	int		scrolled = 0;
	int		extra = 0;
	int		used;
	int		i;
	int		sline;		/* screen line for cursor */
	int		old_topline = curwin->w_topline;

	/*
	 * Decrease topline until:
	 * - it has become 1
	 * - (part of) the cursor line is moved off the screen or
	 * - moved at least 'scrolljump' lines and
	 * - at least 'scrolloff' lines above and below the cursor
	 */
	validate_cheight();
	used = curwin->w_cline_height;
	for (sline = 1; sline < curwin->w_cursor.lnum; ++sline)
	{
		i = plines(curwin->w_cursor.lnum - sline);
		used += i;
		extra += i;
		if (extra <= p_so &&
				   curwin->w_cursor.lnum + sline < curbuf->b_ml.ml_line_count)
			used += plines(curwin->w_cursor.lnum + sline);
		if (used > curwin->w_height)
			break;
		if (curwin->w_cursor.lnum - sline < curwin->w_topline)
			scrolled += i;

		/*
		 * If scrolling is needed, scroll at least 'sj' lines.
		 */
		if ((curwin->w_cursor.lnum - (sline - 1) >= curwin->w_topline ||
									  scrolled >= min_scroll) && extra > p_so)
			break;
	}

	/*
	 * If we don't have enough space, put cursor in the middle.
	 * This makes sure we get the same position when using "k" and "j"
	 * in a small window.
	 */
	if (used > curwin->w_height)
		scroll_cursor_halfway(FALSE);
	else
	{
		/*
		 * If "always" is FALSE, only adjust topline to a lower value, higher
		 * value may happen with wrapping lines
		 */
		if (curwin->w_cursor.lnum - (sline - 1) < curwin->w_topline || always)
			curwin->w_topline = curwin->w_cursor.lnum - (sline - 1);
		if (curwin->w_topline > curwin->w_cursor.lnum)
			curwin->w_topline = curwin->w_cursor.lnum;
		if (curwin->w_topline != old_topline)
			curwin->w_valid &=
					  ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
	}
}

/*
 * Recompute topline to put the cursor at the bottom of the window.
 * Scroll at least "min_scroll" lines.
 * If "set_topbot" is TRUE, set topline and botline first (for "zb").
 * This is messy stuff!!!
 */
	void
scroll_cursor_bot(min_scroll, set_topbot)
	int		min_scroll;
	int		set_topbot;
{
	int			used;
	int			scrolled = 0;
	int			extra = 0;
	int			sline;			/* screen line for cursor from bottom */
	int			i;
	linenr_t	lnum;
	linenr_t	line_count;
	linenr_t	old_topline = curwin->w_topline;
	linenr_t	old_botline = curwin->w_botline;
	linenr_t	old_valid = curwin->w_valid;
	int			old_empty_rows = curwin->w_empty_rows;
	linenr_t	cln;				/* Cursor Line Number */

	cln = curwin->w_cursor.lnum;
	if (set_topbot)
	{
		used = 0;
		curwin->w_botline = cln + 1;
		for (curwin->w_topline = curwin->w_botline;
				curwin->w_topline != 1;
				--curwin->w_topline)
		{
			i = plines(curwin->w_topline - 1);
			if (used + i > curwin->w_height)
				break;
			used += i;
		}
		curwin->w_empty_rows = curwin->w_height - used;
		curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
		if (curwin->w_topline != old_topline)
			curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
	}
	else
		validate_botline();

	validate_cheight();
	used = curwin->w_cline_height;

	if (cln >= curwin->w_botline)
	{
		scrolled = used;
		if (cln == curwin->w_botline)
			scrolled -= curwin->w_empty_rows;
	}

	/*
	 * Stop counting lines to scroll when
	 * - hitting start of the file
	 * - scrolled nothing or at least 'sj' lines
	 * - at least 'so' lines below the cursor
	 * - lines between botline and cursor have been counted
	 */
	for (sline = 1; sline < cln; ++sline)
	{
		if ((((scrolled <= 0 || scrolled >= min_scroll) && extra >= p_so) ||
				cln + sline > curbuf->b_ml.ml_line_count) &&
				cln - sline < curwin->w_botline)
			break;
		i = plines(cln - sline);
		used += i;
		if (used > curwin->w_height)
			break;
		if (cln - sline >= curwin->w_botline)
		{
			scrolled += i;
			if (cln - sline == curwin->w_botline)
				scrolled -= curwin->w_empty_rows;
		}
		if (cln + sline <= curbuf->b_ml.ml_line_count)
		{
			i = plines(cln + sline);
			used += i;
			if (used > curwin->w_height)
				break;
			if (extra < p_so || scrolled < min_scroll)
			{
				extra += i;
				if (cln + sline >= curwin->w_botline)
				{
					scrolled += i;
					if (cln + sline == curwin->w_botline)
						scrolled -= curwin->w_empty_rows;
				}
			}
		}
	}
	/* curwin->w_empty_rows is larger, no need to scroll */
	if (scrolled <= 0)
		line_count = 0;
	/* more than a screenfull, don't scroll but redraw */
	else if (used > curwin->w_height)
		line_count = used;
	/* scroll minimal number of lines */
	else
	{
		for (i = 0, lnum = curwin->w_topline;
				i < scrolled && lnum < curwin->w_botline; ++lnum)
			i += plines(lnum);
		if (i >= scrolled)		/* it's possible to scroll */
			line_count = lnum - curwin->w_topline;
		else				/* below curwin->w_botline, don't scroll */
			line_count = 9999;
	}

	/*
	 * Scroll up if the cursor is off the bottom of the screen a bit.
	 * Otherwise put it at 1/2 of the screen.
	 */
	if (line_count >= curwin->w_height && line_count > min_scroll)
		scroll_cursor_halfway(FALSE);
	else
		scrollup(line_count);

	/*
	 * If topline didn't change we need to restore w_botline and w_empty_rows
	 * (we changed them).
	 * If topline did change, update_screen() will set botline.
	 */
	if (curwin->w_topline == old_topline && set_topbot)
	{
		curwin->w_botline = old_botline;
		curwin->w_empty_rows = old_empty_rows;
		curwin->w_valid = old_valid;
	}
}

/*
 * Recompute topline to put the cursor halfway the window
 * If "atend" is TRUE, also put it halfway at the end of the file.
 */
	void
scroll_cursor_halfway(atend)
	int		atend;
{
	int			above = 0;
	linenr_t	topline;
	int			below = 0;
	linenr_t	botline;
	int			used;
	int			i;
	linenr_t	cln;				/* Cursor Line Number */

	topline = botline = cln = curwin->w_cursor.lnum;
	used = plines(cln);
	while (topline > 1)
	{
		if (below <= above)			/* add a line below the cursor */
		{
			if (botline + 1 <= curbuf->b_ml.ml_line_count)
			{
				i = plines(botline + 1);
				used += i;
				if (used > curwin->w_height)
					break;
				below += i;
				++botline;
			}
			else
			{
				++below;			/* count a "~" line */
				if (atend)
					++used;
			}
		}

		if (below > above)			/* add a line above the cursor */
		{
			i = plines(topline - 1);
			used += i;
			if (used > curwin->w_height)
				break;
			above += i;
			--topline;
		}
	}
	curwin->w_topline = topline;
	curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
}

/*
 * Correct the cursor position so that it is in a part of the screen at least
 * 'so' lines from the top and bottom, if possible.
 * If not possible, put it at the same position as scroll_cursor_halfway().
 * When called topline must be valid!
 */
	void
cursor_correct()
{
	int			above = 0;			/* screen lines above topline */
	linenr_t	topline;
	int			below = 0;			/* screen lines below botline */
	linenr_t	botline;
	int			above_wanted, below_wanted;
	linenr_t	cln;				/* Cursor Line Number */
	int			max_off;

	/*
	 * How many lines we would like to have above/below the cursor depends on
	 * whether the first/last line of the file is on screen.
	 */
	above_wanted = p_so;
	below_wanted = p_so;
	if (curwin->w_topline == 1)
	{
		above_wanted = 0;
		max_off = curwin->w_height / 2;
		if (below_wanted > max_off)
			below_wanted = max_off;
	}
	validate_botline();
	if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1)
	{
		below_wanted = 0;
		max_off = (curwin->w_height - 1) / 2;
		if (above_wanted > max_off)
			above_wanted = max_off;
	}

	/*
	 * If there are sufficient file-lines above and below the cursor, we can
	 * return now.
	 */
	cln = curwin->w_cursor.lnum;
	if (cln >= curwin->w_topline + above_wanted &&
									  cln < curwin->w_botline - below_wanted)
		return;

	/*
	 * Narrow down the area where the cursor can be put by taking lines from
	 * the top and the bottom until:
	 * - the desired context lines are found
	 * - the lines from the top is past the lines from the bottom
	 */
	topline = curwin->w_topline;
	botline = curwin->w_botline - 1;
	while ((above < above_wanted || below < below_wanted) && topline < botline)
	{
		if (below < below_wanted && (below <= above || above >= above_wanted))
		{
			below += plines(botline);
			--botline;
		}
		if (above < above_wanted && (above < below || below >= below_wanted))
		{
			above += plines(topline);
			++topline;
		}
	}
	if (topline == botline || botline == 0)
		curwin->w_cursor.lnum = topline;
	else if (topline > botline)
		curwin->w_cursor.lnum = botline;
	else
	{
		if (cln < topline && curwin->w_topline > 1)
		{
			curwin->w_cursor.lnum = topline;
			curwin->w_valid &=
							~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
		}
		if (cln > botline && curwin->w_botline <= curbuf->b_ml.ml_line_count)
		{
			curwin->w_cursor.lnum = botline;
			curwin->w_valid &=
							~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW);
		}
	}
}


/*
 * Check if the cursor has moved.  Set the w_valid flag accordingly.
 */
	static void
check_cursor_moved(wp)
	WIN		*wp;
{
	if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum)
	{
		wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
												   |VALID_CHEIGHT|VALID_CROW);
		wp->w_valid_cursor = wp->w_cursor;
	}
	else if (wp->w_cursor.col != wp->w_valid_cursor.col)
	{
		wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
		wp->w_valid_cursor.col = wp->w_cursor.col;
	}
}

/*
 * Call this function when the length of the cursor line (in screen
 * characters) has changed, and the change is before the cursor.
 * Need to take care of w_botline separately!
 */
	void
changed_cline_bef_curs()
{
	curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CHEIGHT);
}

/*
 * Call this function when the length of the cursor line (in screen
 * characters) has changed, and the position of the cursor doesn't change.
 * Need to take care of w_botline separately!
 */
	void
changed_cline_aft_curs()
{
	curwin->w_valid &= ~VALID_CHEIGHT;
}

/*
 * Call this function when the length of a line (in screen characters) above
 * the cursor have changed.
 * Need to take care of w_botline separately!
 */
	void
changed_line_abv_curs()
{
	curwin->w_valid &=
			  ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL|VALID_CROW|VALID_CHEIGHT);
}

/*
 * Set wp->w_topline to a certain number.
 */
	void
set_topline(wp, lnum)
	WIN				*wp;
	linenr_t		lnum;
{
	/* Approximate the value of w_botline */
	wp->w_botline += lnum - wp->w_topline;
	wp->w_topline = lnum;
	wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}

/*
 * Make sure the value of curwin->w_botline is valid.
 */
	void
validate_botline()
{
	if (!(curwin->w_valid & VALID_BOTLINE))
		comp_botline();
}

/*
 * Mark curwin->w_botline as invalid (because of some change in the buffer).
 */
	void
invalidate_botline()
{
	curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
}

	void
invalidate_botline_win(wp)
	WIN		*wp;
{
	wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP);
}

/*
 * Mark curwin->w_botline as approximated (because of some small change in the
 * buffer).
 */
	void
approximate_botline()
{
	curwin->w_valid &= ~VALID_BOTLINE;
}

/*
 * Return TRUE if curwin->w_botline is valid.
 */
	int
botline_valid()
{
	return (curwin->w_valid & VALID_BOTLINE);
}

/*
 * Return TRUE if curwin->w_botline is valid or approximated.
 */
	int
botline_approximated()
{
	return (curwin->w_valid & VALID_BOTLINE_AP);
}

/*
 * Return TRUE if curwin->w_wrow and curwin->w_wcol are valid.
 */
	int
cursor_valid()
{
	check_cursor_moved(curwin);
	return ((curwin->w_valid & (VALID_WROW|VALID_WCOL)) ==
													  (VALID_WROW|VALID_WCOL));
}

/*
 * Validate cursor position.  Makes sure w_wrow and w_wcol are valid.
 * w_topline must be valid, you may need to call update_topline() first!
 */
	void
validate_cursor()
{
	check_cursor_moved(curwin);
	if ((curwin->w_valid & (VALID_WCOL|VALID_WROW)) != (VALID_WCOL|VALID_WROW))
		curs_columns(TRUE);
}

/*
 * validate w_cline_row.
 */
	void
validate_cline_row()
{
	/*
	 * First make sure that w_topline is valid (after moving the cursor).
	 */
	update_topline();
	check_cursor_moved(curwin);
	if (!(curwin->w_valid & VALID_CROW))
		curs_rows(FALSE);
}

/*
 * Check if w_cline_row and w_cline_height are valid, or can be made valid.
 * Can be called when topline and botline have not been updated.
 * Used to decide to redraw or keep the window update.
 *
 * Return OK if w_cline_row is valid.
 */
	int
may_validate_crow()
{
	if (curwin->w_cursor.lnum < curwin->w_topline ||
			curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count ||
			!(curwin->w_valid & (VALID_BOTLINE|VALID_BOTLINE_AP)) ||
			curwin->w_cursor.lnum >= curwin->w_botline)
		return FAIL;

	check_cursor_moved(curwin);
	if ((curwin->w_valid & (VALID_CROW|VALID_CHEIGHT)) !=
						  (VALID_CROW|VALID_CHEIGHT))
		curs_rows(TRUE);
	return OK;
}

/*
 * Compute curwin->w_cline_row and curwin->w_cline_height.
 * Must only be called when w_topine is valid!
 *
 * Returns OK when cursor is in the window, FAIL when it isn't.
 */
	static void
curs_rows(do_botline)
	int		do_botline;			/* also compute w_botline */
{
	linenr_t	lnum;
	int			i;
	int			lsize_invalid;

	/* Check if curwin->w_lsize[] is invalid */
	lsize_invalid = (!redrawing() || curwin->w_lsize_valid == 0 ||
								curwin->w_lsize_lnum[0] != curwin->w_topline);
	i = 0;
	curwin->w_cline_row = 0;
	for (lnum = curwin->w_topline; lnum < curwin->w_cursor.lnum; ++lnum)
		if (lsize_invalid)
			curwin->w_cline_row += plines(lnum);
		else
			curwin->w_cline_row += curwin->w_lsize[i++];

	check_cursor_moved(curwin);
	if (!(curwin->w_valid & VALID_CHEIGHT))
	{
		if (lsize_invalid)
			curwin->w_cline_height = plines(lnum);
		/* Check for a line that is too long to fit on the last screen line. */
		else if (i > curwin->w_lsize_valid)
			curwin->w_cline_height = 0;
		else
			curwin->w_cline_height = curwin->w_lsize[i];
	}

	curwin->w_valid |= VALID_CROW|VALID_CHEIGHT;

	/* validate botline too, if update_screen doesn't do it */
	if (do_botline && lsize_invalid)
		validate_botline();
}

/*
 * Validate curwin->w_virtcol only.
 */
	void
validate_virtcol()
{
	validate_virtcol_win(curwin);
}

/*
 * Validate wp->w_virtcol only.
 */
	static void
validate_virtcol_win(wp)
	WIN		*wp;
{
	check_cursor_moved(wp);
	if (!(wp->w_valid & VALID_VIRTCOL))
	{
		getvcol(wp, &wp->w_cursor, NULL, &(wp->w_virtcol), NULL);
		wp->w_valid |= VALID_VIRTCOL;
	}
}

/*
 * Validate curwin->w_cline_height only.
 */
	void
validate_cheight()
{
	check_cursor_moved(curwin);
	if (!(curwin->w_valid & VALID_CHEIGHT))
	{
		curwin->w_cline_height = plines(curwin->w_cursor.lnum);
		curwin->w_valid |= VALID_CHEIGHT;
	}
}

/*
 * validate w_wcol and w_virtcol only.  Only for when 'wrap' on!
 */
	void
validate_cursor_col()
{
	validate_virtcol();
	if (!(curwin->w_valid & VALID_WCOL))
	{
		curwin->w_wcol = curwin->w_virtcol;
		if (curwin->w_p_nu)
			curwin->w_wcol += 8;

		/* long line wrapping, adjust curwin->w_wrow */
		if (curwin->w_p_wrap && curwin->w_wcol >= Columns)
			curwin->w_wcol = curwin->w_wcol % Columns;

		curwin->w_valid |= VALID_WCOL;
	}
}

/*
 * compute curwin->w_wcol and curwin->w_virtcol.
 * Also updates curwin->w_wrow and curwin->w_cline_row.
 */
	void
curs_columns(scroll)
	int scroll;			/* when TRUE, may scroll horizontally */
{
	int		diff;
	int		extra;
	int		new_leftcol;
	colnr_t	startcol;
	colnr_t endcol;

	/*
	 * First make sure that w_topline is valid (after moving the cursor).
	 */
	update_topline();

	/*
	 * Next make sure that w_cline_row is valid.
	 */
	if (!(curwin->w_valid & VALID_CROW))
		curs_rows(FALSE);

	/*
	 * Compute the number of virtual columns.
	 */
	getvcol(curwin, &curwin->w_cursor,
								&startcol, &(curwin->w_virtcol), &endcol);

		/* remove '$' from change command when cursor moves onto it */
	if (startcol > dollar_vcol)
		dollar_vcol = 0;

	curwin->w_wcol = curwin->w_virtcol;
	if (curwin->w_p_nu)
	{
		curwin->w_wcol += 8;
		endcol += 8;
	}

	/*
	 * Now compute w_wrow, counting screen lines from w_cline_row.
	 */
	curwin->w_wrow = curwin->w_cline_row;

	if (curwin->w_p_wrap)		/* long line wrapping, adjust curwin->w_wrow */
	{
		while (curwin->w_wcol >= Columns)
		{
			curwin->w_wcol -= Columns;
			curwin->w_wrow++;
		}
	}
	else if (scroll)	/* no line wrapping, compute curwin->w_leftcol if
						 * scrolling is on.  If scrolling is off,
						 * curwin->w_leftcol is assumed to be 0 */
	{
						/* If Cursor is left of the screen, scroll rightwards */
						/* If Cursor is right of the screen, scroll leftwards */
		if ((extra = (int)startcol - (int)curwin->w_leftcol) < 0 ||
			 (extra = (int)endcol - (int)(curwin->w_leftcol + Columns) + 1) > 0)
		{
			if (extra < 0)
				diff = -extra;
			else
				diff = extra;

				/* far off, put cursor in middle of window */
			if (p_ss == 0 || diff >= Columns / 2)
				new_leftcol = curwin->w_wcol - Columns / 2;
			else
			{
				if (diff < p_ss)
					diff = p_ss;
				if (extra < 0)
					new_leftcol = curwin->w_leftcol - diff;
				else
					new_leftcol = curwin->w_leftcol + diff;
			}
			if (new_leftcol < 0)
				curwin->w_leftcol = 0;
			else
				curwin->w_leftcol = new_leftcol;
					/* screen has to be redrawn with new curwin->w_leftcol */
			redraw_later(NOT_VALID);
		}
		curwin->w_wcol -= curwin->w_leftcol;
	}
	else if (curwin->w_wcol > (int)curwin->w_leftcol)
		curwin->w_wcol -= curwin->w_leftcol;
	else
		curwin->w_wcol = 0;

		/* Cursor past end of screen */
		/* happens with line that does not fit on screen */
	if (curwin->w_wrow > curwin->w_height - 1)
		curwin->w_wrow = curwin->w_height - 1;

	curwin->w_valid |= VALID_WCOL|VALID_WROW;
}

/*
 * Scroll up 'line_count' lines.
 */
	void
scrolldown(line_count)
	long	line_count;
{
	long	done = 0;			/* total # of physical lines done */
	int		wrow;
	int		moved = FALSE;

	validate_cursor();			/* w_wrow needs to be valid */
	while (line_count--)
	{
		if (curwin->w_topline == 1)
			break;
		done += plines(--curwin->w_topline);
		--curwin->w_botline;			/* approximate w_botline */
		curwin->w_valid &= ~VALID_BOTLINE;
	}
	curwin->w_wrow += done;				/* keep w_wrow updated */
	curwin->w_cline_row += done;		/* keep w_cline_row updated */

	/*
	 * Compute the row number of the last row of the cursor line
	 * and move it onto the screen.
	 */
	wrow = curwin->w_wrow;
	if (curwin->w_p_wrap)
	{
		validate_virtcol();
		validate_cheight();
		wrow += curwin->w_cline_height - 1 - curwin->w_virtcol / Columns;
	}
	while (wrow >= curwin->w_height - p_so && curwin->w_cursor.lnum > 1)
	{
		wrow -= plines(curwin->w_cursor.lnum--);
		curwin->w_valid &=
			  ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
		moved = TRUE;
	}
	if (moved)
		coladvance(curwin->w_curswant);
}

	void
scrollup(line_count)
	long	line_count;
{
	curwin->w_topline += line_count;
	curwin->w_botline += line_count;		/* approximate w_botline */
	curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
	if (curwin->w_topline > curbuf->b_ml.ml_line_count)
		curwin->w_topline = curbuf->b_ml.ml_line_count;
	if (curwin->w_cursor.lnum < curwin->w_topline)
	{
		curwin->w_cursor.lnum = curwin->w_topline;
		curwin->w_valid &=
			  ~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
		coladvance(curwin->w_curswant);
	}
}

/*
 * Scroll the screen one line down, but don't do it if it would move the
 * cursor off the screen.
 */
	void
scrolldown_clamp()
{
	int		end_row;

	if (curwin->w_topline == 1)
		return;

	validate_cursor();		/* w_wrow needs to be valid */

	/*
	 * Compute the row number of the last row of the cursor line
	 * and make sure it doesn't go off the screen. Make sure the cursor
	 * doesn't go past 'scrolloff' lines from the screen end.
	 */
	end_row = curwin->w_wrow + plines(curwin->w_topline - 1);
	if (curwin->w_p_wrap)
	{
		validate_cheight();
		validate_virtcol();
		end_row += curwin->w_cline_height - 1 - curwin->w_virtcol / Columns;
	}
	if (end_row < curwin->w_height - p_so)
	{
		--curwin->w_topline;
		--curwin->w_botline;		/* approximate w_botline */
		curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
	}
}

/*
 * Scroll the screen one line up, but don't do it if it would move the cursor
 * off the screen.
 */
	void
scrollup_clamp()
{
	int		start_row;

	if (curwin->w_topline == curbuf->b_ml.ml_line_count)
		return;

	validate_cursor();		/* w_wrow needs to be valid */

	/*
	 * Compute the row number of the first row of the cursor line
	 * and make sure it doesn't go off the screen. Make sure the cursor
	 * doesn't go before 'scrolloff' lines from the screen start.
	 */
	start_row = curwin->w_wrow - plines(curwin->w_topline);
	if (curwin->w_p_wrap)
	{
		validate_virtcol();
		start_row -= curwin->w_virtcol / Columns;
	}
	if (start_row >= p_so)
	{
		++curwin->w_topline;
		++curwin->w_botline;			/* approximate w_botline */
		curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
	}
}

/*
 * insert 'line_count' lines at 'row' in window 'wp'
 * if 'invalid' is TRUE the wp->w_lsize_lnum[] is invalidated.
 * if 'mayclear' is TRUE the screen will be cleared if it is faster than
 * scrolling.
 * Returns FAIL if the lines are not inserted, OK for success.
 */
	int
win_ins_lines(wp, row, line_count, invalid, mayclear)
	WIN		*wp;
	int		row;
	int		line_count;
	int		invalid;
	int		mayclear;
{
	int		did_delete;
	int		nextrow;
	int		lastrow;
	int		retval;

	if (invalid)
		wp->w_lsize_valid = 0;

	if (!redrawing() || line_count <= 0 || wp->w_height < 5)
		return FAIL;

	if (line_count > wp->w_height - row)
		line_count = wp->w_height - row;

	if (mayclear && Rows - line_count < 5)	/* only a few lines left: redraw is faster */
	{
		screenclear();		/* will set wp->w_lsize_valid to 0 */
		return FAIL;
	}

	/*
	 * Delete all remaining lines
	 */
	if (row + line_count >= wp->w_height)
	{
		screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
												0, (int)Columns, ' ', ' ', 0);
		return OK;
	}

	/*
	 * when scrolling, the message on the command line should be cleared,
	 * otherwise it will stay there forever.
	 */
	clear_cmdline = TRUE;

	/*
	 * if the terminal can set a scroll region, use that
	 */
	if (scroll_region)
	{
		scroll_region_set(wp, row);
		retval = screen_ins_lines(wp->w_winpos + row, 0, line_count,
														  wp->w_height - row);
		scroll_region_reset();
		return retval;
	}

	if (wp->w_next && p_tf)		/* don't delete/insert on fast terminal */
		return FAIL;

	/*
	 * If there is a next window or a status line, we first try to delete the
	 * lines at the bottom to avoid messing what is after the window.
	 * If this fails and there are following windows, don't do anything to avoid
	 * messing up those windows, better just redraw.
	 */
	did_delete = FALSE;
	if (wp->w_next || wp->w_status_height)
	{
		if (screen_del_lines(0, wp->w_winpos + wp->w_height - line_count,
										  line_count, (int)Rows, FALSE) == OK)
			did_delete = TRUE;
		else if (wp->w_next)
			return FAIL;
	}
	/*
	 * if no lines deleted, blank the lines that will end up below the window
	 */
	if (!did_delete)
	{
		wp->w_redr_status = TRUE;
		redraw_cmdline = TRUE;
		nextrow = wp->w_winpos + wp->w_height + wp->w_status_height;
		lastrow = nextrow + line_count;
		if (lastrow > Rows)
			lastrow = Rows;
		screen_fill(nextrow - line_count, lastrow - line_count,
												0, (int)Columns, ' ', ' ', 0);
	}

	if (screen_ins_lines(0, wp->w_winpos + row, line_count, (int)Rows) == FAIL)
	{
			/* deletion will have messed up other windows */
		if (did_delete)
		{
			wp->w_redr_status = TRUE;
			win_rest_invalid(wp->w_next);
		}
		return FAIL;
	}

	return OK;
}

/*
 * delete 'line_count' lines at 'row' in window 'wp'
 * If 'invalid' is TRUE curwin->w_lsize_lnum[] is invalidated.
 * If 'mayclear' is TRUE the screen will be cleared if it is faster than
 * scrolling
 * Return OK for success, FAIL if the lines are not deleted.
 */
	int
win_del_lines(wp, row, line_count, invalid, mayclear)
	WIN				*wp;
	int 			row;
	int 			line_count;
	int				invalid;
	int				mayclear;
{
	int			retval;

	if (invalid)
		wp->w_lsize_valid = 0;

	if (!redrawing() || line_count <= 0)
		return FAIL;

	if (line_count > wp->w_height - row)
		line_count = wp->w_height - row;

	/* only a few lines left: redraw is faster */
	if (mayclear && Rows - line_count < 5)
	{
		screenclear();		/* will set wp->w_lsize_valid to 0 */
		return FAIL;
	}

	/*
	 * Delete all remaining lines
	 */
	if (row + line_count >= wp->w_height)
	{
		screen_fill(wp->w_winpos + row, wp->w_winpos + wp->w_height,
												0, (int)Columns, ' ', ' ', 0);
		return OK;
	}

	/*
	 * when scrolling, the message on the command line should be cleared,
	 * otherwise it will stay there forever.
	 */
	clear_cmdline = TRUE;

	/*
	 * if the terminal can set a scroll region, use that
	 */
	if (scroll_region)
	{
		scroll_region_set(wp, row);
		retval = screen_del_lines(wp->w_winpos + row, 0, line_count,
												   wp->w_height - row, FALSE);
		scroll_region_reset();
		return retval;
	}

	if (wp->w_next && p_tf)		/* don't delete/insert on fast terminal */
		return FAIL;

	if (screen_del_lines(0, wp->w_winpos + row, line_count,
													(int)Rows, FALSE) == FAIL)
		return FAIL;

	/*
	 * If there are windows or status lines below, try to put them at the
	 * correct place. If we can't do that, they have to be redrawn.
	 */
	if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1)
	{
		if (screen_ins_lines(0, wp->w_winpos + wp->w_height - line_count,
											   line_count, (int)Rows) == FAIL)
		{
			wp->w_redr_status = TRUE;
			win_rest_invalid(wp->w_next);
		}
	}
	/*
	 * If this is the last window and there is no status line, redraw the
	 * command line later.
	 */
	else
		redraw_cmdline = TRUE;
	return OK;
}

/*
 * window 'wp' and everything after it is messed up, mark it for redraw
 */
	void
win_rest_invalid(wp)
	WIN			*wp;
{
	while (wp)
	{
		wp->w_lsize_valid = 0;
		wp->w_redr_type = NOT_VALID;
		wp->w_redr_status = TRUE;
		wp = wp->w_next;
	}
	redraw_cmdline = TRUE;
}

/*
 * The rest of the routines in this file perform screen manipulations. The
 * given operation is performed physically on the screen. The corresponding
 * change is also made to the internal screen image. In this way, the editor
 * anticipates the effect of editing changes on the appearance of the screen.
 * That way, when we call screenupdate a complete redraw isn't usually
 * necessary. Another advantage is that we can keep adding code to anticipate
 * screen changes, and in the meantime, everything still works.
 */

/*
 * types for inserting or deleting lines
 */
#define USE_T_CAL	1
#define USE_T_CDL	2
#define USE_T_AL	3
#define USE_T_CE	4
#define USE_T_DL	5
#define USE_T_SR	6
#define USE_NL		7
#define USE_T_CD	8

/*
 * insert lines on the screen and update NextScreen
 * 'end' is the line after the scrolled part. Normally it is Rows.
 * When scrolling region used 'off' is the offset from the top for the region.
 * 'row' and 'end' are relative to the start of the region.
 *
 * return FAIL for failure, OK for success.
 */
	static int
screen_ins_lines(off, row, line_count, end)
	int			off;
	int 		row;
	int 		line_count;
	int			end;
{
	int 		i;
	int 		j;
	char_u		*temp;
	int			cursor_row;
	int			type;
	int			result_empty;

	/*
	 * FAIL if
	 * - there is no valid screen
	 * - the screen has to be redrawn completely
	 * - the line count is less than one
	 * - the line count is more than 'ttyscroll'
	 */
	if (!screen_valid(TRUE) || line_count <= 0 || line_count > p_ttyscroll)
		return FAIL;

	/*
	 * There are seven ways to insert lines:
	 * 1. Use T_CD (clear to end of display) if it exists and the result of
	 *	  the insert is just empty lines
	 * 2. Use T_CAL (insert multiple lines) if it exists and T_AL is not
	 *    present or line_count > 1. It looks better if we do all the inserts
	 *    at once.
	 * 3. Use T_CDL (delete multiple lines) if it exists and the result of the
	 *    insert is just empty lines and T_CE is not present or line_count >
	 *    1.
	 * 4. Use T_AL (insert line) if it exists.
	 * 5. Use T_CE (erase line) if it exists and the result of the insert is
	 *    just empty lines.
	 * 6. Use T_DL (delete line) if it exists and the result of the insert is
	 *    just empty lines.
	 * 7. Use T_SR (scroll reverse) if it exists and inserting at row 0 and
	 *    the 'da' flag is not set or we have clear line capability.
	 *
	 * Careful: In a hpterm scroll reverse doesn't work as expected, it moves
	 * the scrollbar for the window. It does have insert line, use that if it
	 * exists.
	 */
	result_empty = (row + line_count >= end);
	if (*T_CD != NUL && result_empty)
		type = USE_T_CD;
	else if (*T_CAL != NUL && (line_count > 1 || *T_AL == NUL))
		type = USE_T_CAL;
	else if (*T_CDL != NUL && result_empty && (line_count > 1 || *T_CE == NUL))
		type = USE_T_CDL;
	else if (*T_AL != NUL)
		type = USE_T_AL;
	else if (*T_CE != NUL && result_empty)
		type = USE_T_CE;
	else if (*T_DL != NUL && result_empty)
		type = USE_T_DL;
	else if (*T_SR != NUL && row == 0 && (*T_DA == NUL || *T_CE))
		type = USE_T_SR;
	else
		return FAIL;

	/*
	 * For clearing the lines screen_del_lines is used. This will also take
	 * care of t_db if necessary.
	 */
	if (type == USE_T_CD || type == USE_T_CDL ||
										 type == USE_T_CE || type == USE_T_DL)
		return screen_del_lines(off, row, line_count, end, FALSE);

	/*
	 * If text is retained below the screen, first clear or delete as many
	 * lines at the bottom of the window as are about to be inserted so that
	 * the deleted lines won't later surface during a screen_del_lines.
	 */
	if (*T_DB)
		screen_del_lines(off, end - line_count, line_count, end, FALSE);

	if (*T_CCS != NUL)     /* cursor relative to region */
		cursor_row = row;
	else
		cursor_row = row + off;

	/*
	 * Shift LinePointers line_count down to reflect the inserted lines.
	 * Clear the inserted lines in NextScreen.
	 */
	row += off;
	end += off;
	for (i = 0; i < line_count; ++i)
	{
		j = end - 1 - i;
		temp = LinePointers[j];
		while ((j -= line_count) >= row)
			LinePointers[j + line_count] = LinePointers[j];
		LinePointers[j + line_count] = temp;
		lineclear(temp);
	}

	screen_stop_highlight();
    windgoto(cursor_row, 0);
    if (type == USE_T_CAL)
	{
		term_append_lines(line_count);
		screen_start();			/* don't know where cursor is now */
	}
    else
	{
        for (i = 0; i < line_count; i++)
        {
			if (type == USE_T_AL)
			{
				if (i && cursor_row != 0)
					windgoto(cursor_row, 0);
				outstr(T_AL);
			}
			else  /* type == USE_T_SR */
				outstr(T_SR);
			screen_start();			/* don't know where cursor is now */
        }
    }

	/*
	 * With scroll-reverse and 'da' flag set we need to clear the lines that
	 * have been scrolled down into the region.
	 */
	if (type == USE_T_SR && *T_DA)
	{
        for (i = 0; i < line_count; ++i)
        {
			windgoto(off + i, 0);
			outstr(T_CE);
			screen_start();			/* don't know where cursor is now */
		}
	}

	return OK;
}

/*
 * delete lines on the screen and update NextScreen
 * 'end' is the line after the scrolled part. Normally it is Rows.
 * When scrolling region used 'off' is the offset from the top for the region.
 * 'row' and 'end' are relative to the start of the region.
 *
 * Return OK for success, FAIL if the lines are not deleted.
 */
	int
screen_del_lines(off, row, line_count, end, force)
	int				off;
	int 			row;
	int 			line_count;
	int				end;
	int				force;		/* even when line_count > p_ttyscroll */
{
	int 		j;
	int 		i;
	char_u		*temp;
	int			cursor_row;
	int			cursor_end;
	int			result_empty;	/* result is empty until end of region */
	int			can_delete;		/* deleting line codes can be used */
	int			type;

	/*
	 * FAIL if
	 * - there is no valid screen
	 * - the screen has to be redrawn completely
	 * - the line count is less than one
	 * - the line count is more than 'ttyscroll'
	 */
	if (!screen_valid(TRUE) || line_count <= 0 ||
										 (!force && line_count > p_ttyscroll))
		return FAIL;

	/*
	 * Check if the rest of the current region will become empty.
	 */
	result_empty = row + line_count >= end;

	/*
	 * We can delete lines only when 'db' flag not set or when 'ce' option
	 * available.
	 */
	can_delete = (*T_DB == NUL || *T_CE);

	/*
	 * There are four ways to delete lines:
	 * 1. Use T_CD if it exists and the result is empty.
	 * 2. Use newlines if row == 0 and count == 1 or T_CDL does not exist.
	 * 3. Use T_CDL (delete multiple lines) if it exists and line_count > 1 or
	 *    none of the other ways work.
	 * 4. Use T_CE (erase line) if the result is empty.
	 * 5. Use T_DL (delete line) if it exists.
	 */
	if (*T_CD != NUL && result_empty)
		type = USE_T_CD;
#ifdef BEBOX
	/*
	 * USE_NL does not seem to work in Terminal of DR8 so we set T_DB="" in
	 * its internal termcap... this works okay for tests which test *T_DB !=
	 * NUL.  It has the disadvantage that the user cannot use any :set t_*
	 * command to get T_DB (back) to empty_option, only :set term=... will do
	 * the trick...
	 * Anyway, this hack will hopefully go away with the next OS release.
	 * (Olaf Seibert)
	 */
	else if (row == 0 && T_DB == empty_option
										&& (line_count == 1 || *T_CDL == NUL))
#else
	else if (row == 0 && (line_count == 1 || *T_CDL == NUL))
#endif
		type = USE_NL;
	else if (*T_CDL != NUL && line_count > 1 && can_delete)
		type = USE_T_CDL;
	else if (*T_CE != NUL && result_empty)
		type = USE_T_CE;
	else if (*T_DL != NUL && can_delete)
		type = USE_T_DL;
	else if (*T_CDL != NUL && can_delete)
		type = USE_T_CDL;
	else
		return FAIL;

	if (*T_CCS != NUL)		/* cursor relative to region */
	{
		cursor_row = row;
		cursor_end = end;
	}
	else
	{
		cursor_row = row + off;
		cursor_end = end + off;
	}

	/*
	 * Now shift LinePointers line_count up to reflect the deleted lines.
	 * Clear the inserted lines in NextScreen.
	 */
	row += off;
	end += off;
	for (i = 0; i < line_count; ++i)
	{
		j = row + i;
		temp = LinePointers[j];
		while ((j += line_count) <= end - 1)
			LinePointers[j - line_count] = LinePointers[j];
		LinePointers[j - line_count] = temp;
		lineclear(temp);
	}

	/* delete the lines */
	screen_stop_highlight();

	if (type == USE_T_CD)
	{
		windgoto(cursor_row, 0);
		outstr(T_CD);
		screen_start();					/* don't know where cursor is now */
	}
	else if (type == USE_T_CDL)
	{
		windgoto(cursor_row, 0);
		term_delete_lines(line_count);
		screen_start();					/* don't know where cursor is now */
	}
		/*
		 * Deleting lines at top of the screen or scroll region: Just scroll
		 * the whole screen (scroll region) up by outputting newlines on the
		 * last line.
		 */
	else if (type == USE_NL)
	{
		windgoto(cursor_end - 1, 0);
		for (i = line_count; --i >= 0; )
			outchar('\n');				/* cursor will remain on same line */
	}
	else
	{
		for (i = line_count; --i >= 0; )
		{
			if (type == USE_T_DL)
			{
				windgoto(cursor_row, 0);
				outstr(T_DL);           /* delete a line */
			}
			else /* type == USE_T_CE */
			{
				windgoto(cursor_row + i, 0);
				outstr(T_CE);           /* erase a line */
			}
			screen_start();				/* don't know where cursor is now */
		}
	}

	/*
	 * If the 'db' flag is set, we need to clear the lines that have been
	 * scrolled up at the bottom of the region.
	 */
	if (*T_DB && (type == USE_T_DL || type == USE_T_CDL))
	{
		for (i = line_count; i > 0; --i)
		{
			windgoto(cursor_end - i, 0);
			outstr(T_CE);           	/* erase a line */
			screen_start();				/* don't know where cursor is now */
		}
	}
	return OK;
}

/*
 * show the current mode and ruler
 *
 * If clear_cmdline is TRUE, clear the rest of the cmdline.
 * If clear_cmdline is FALSE there may be a message there that needs to be
 * cleared only if a mode is shown.
 * Return the length of the message (0 if no message).
 */
	int
showmode()
{
	int		need_clear = FALSE;
	int		length = 0;
	int		do_mode;
	int		attr;
#ifdef INSERT_EXPAND
	int		sub_attr;
#endif

	do_mode = (p_smd && ((State & INSERT) || restart_edit || VIsual_active));
	if (do_mode || Recording)
	{
		/*
		 * Don't show mode right now, when not redrawing or inside a mapping.
		 * Call char_avail() only when we are going to show something, because
		 * it takes a bit of time.
		 */
		if (!redrawing() || (char_avail() && !KeyTyped))
		{
			redraw_cmdline = TRUE;			/* show mode later */
			return 0;
		}

		check_for_delay(FALSE);
		msg_didout = FALSE;				/* never scroll up */
		msg_col = 0;
		gotocmdline(FALSE);
		attr = highlight_attr[HLF_CM];	/* Highlight mode */
		if (do_mode)
		{
			MSG_PUTS_ATTR("--", attr);
#ifdef INSERT_EXPAND
			if (edit_submode != NULL)		/* CTRL-X in Insert mode */
			{
				msg_puts_attr(edit_submode, attr);
				if (edit_submode_extra != NULL)
				{
					MSG_PUTS_ATTR(" ", attr);	/* add a space in between */
					if (edit_submode_highl >= 0)
						sub_attr = highlight_attr[edit_submode_highl];
					else
						sub_attr = attr;
					msg_puts_attr(edit_submode_extra, sub_attr);
				}
			}
			else
#endif
			{
				if (State == INSERT)
				{
#ifdef RIGHTLEFT
					if (p_ri)
						MSG_PUTS_ATTR(" REVERSE", attr);
#endif
					MSG_PUTS_ATTR(" INSERT", attr);
				}
				else if (State == REPLACE)
					MSG_PUTS_ATTR(" REPLACE", attr);
				else if (restart_edit == 'I')
					MSG_PUTS_ATTR(" (insert)", attr);
				else if (restart_edit == 'R')
					MSG_PUTS_ATTR(" (replace)", attr);
#ifdef RIGHTLEFT
				if (p_hkmap)
					MSG_PUTS_ATTR(" Hebrew", attr);
#endif
				if ((State & INSERT) && p_paste)
					MSG_PUTS_ATTR(" (paste)", attr);
				if (VIsual_active)
				{
					MSG_PUTS_ATTR(" VISUAL", attr);
					if (VIsual_mode == Ctrl('V'))
						MSG_PUTS_ATTR(" BLOCK", attr);
					else if (VIsual_mode == 'V')
						MSG_PUTS_ATTR(" LINE", attr);
				}
			}
			MSG_PUTS_ATTR(" --", attr);
			need_clear = TRUE;
		}
		if (Recording)
		{
			MSG_PUTS_ATTR("recording", attr);
			need_clear = TRUE;
		}
		if (need_clear || clear_cmdline)
			msg_clr_eos();
		msg_didout = FALSE;				/* overwrite this message */
		length = msg_col;
		msg_col = 0;
	}
	else if (clear_cmdline)				/* just clear anything */
	{
		msg_row = cmdline_row;
		msg_col = 0;
		msg_clr_eos();					/* will reset clear_cmdline */
	}
	win_redr_ruler(lastwin, TRUE);
	redraw_cmdline = FALSE;

	return length;
}

/*
 * delete mode message
 */
	void
unshowmode()
{
	/*
	 * Don't delete it right now, when not redrawing or insided a mapping.
	 */
	if (!redrawing() || (char_avail() && !KeyTyped))
		redraw_cmdline = TRUE;			/* delete mode later */
	else if (Recording)
		MSG("recording");
	else
		MSG("");
}

	static int
highlight_status(attr)
	int		*attr;
{
	*attr = highlight_attr[HLF_S];
	if (*attr)							/* can use highlighting */
		return ' ';
	return '=';
}

/*
 * if ruler option is set: show current cursor position
 * if always is FALSE, only print if position has changed
 */
	void
showruler(always)
	int		always;
{
	win_redr_ruler(curwin, always);
}

	void
win_redr_ruler(wp, always)
	WIN		*wp;
	int		always;
{
	static linenr_t	old_lnum = 0;
	static colnr_t	old_col = 0;
	char_u			buffer[30];
	int				row;
	int				fillchar;
	int				attr;
	int				empty_line = FALSE;

	if (!p_ru)		/* 'ruler' off, don't do anything */
		return;

	/*
	 * Check if cursor.lnum is valid, since win_redr_ruler() may be called
	 * after deleting lines, before cursor.lnum is corrected.
	 */
	if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count)
		return;

	/*
	 * Need to update on an empty line always, since we don't know if there
	 * was a character previously (changing column "1" to "0-1").
	 */
	if (*ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE) == NUL)
		empty_line = TRUE;

	/* Note: don't update w_virtcol, assume that it's correct when the ruler
	 * needs to be redrawn */
	if ((redraw_cmdline || always || empty_line ||
				wp->w_cursor.lnum != old_lnum || wp->w_virtcol != old_col))
	{
		cursor_off();
		if (wp->w_status_height)
		{
			row = wp->w_winpos + wp->w_height;
			fillchar = highlight_status(&attr);
		}
		else
		{
			row = Rows - 1;
			fillchar = ' ';
			attr = 0;
		}
		/*
		 * Some sprintfs return the length, some return a pointer.
		 * To avoid portability problems we use strlen() here.
		 */
		sprintf((char *)buffer, "%ld,",
				(wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
					? 0L
					: (long)(wp->w_cursor.lnum));
		col_print(buffer + STRLEN(buffer),
				!(State & INSERT) && empty_line
					? 0
					: (int)wp->w_cursor.col + 1,
				(int)wp->w_virtcol + 1);

		screen_puts(buffer, row, ru_col, attr);
		screen_fill(row, row + 1, ru_col + (int)STRLEN(buffer),
									  (int)Columns, fillchar, fillchar, attr);
		old_lnum = wp->w_cursor.lnum;
		old_col = wp->w_virtcol;
	}
}

/*
 * Check if there should be a delay.  Used before clearing or redrawing the
 * screen or the command line.
 */
	void
check_for_delay(check_msg_scroll)
	int		check_msg_scroll;
{
	if (emsg_on_display || (check_msg_scroll && msg_scroll)
#ifdef SLEEP_IN_EMSG
			|| need_sleep
#endif
							)
	{
		ui_delay(1000L, TRUE);
		emsg_on_display = FALSE;
		if (check_msg_scroll)
			msg_scroll = FALSE;
#ifdef SLEEP_IN_EMSG
		need_sleep = FALSE;
#endif
	}
}

/*
 * screen_valid -  allocate screen buffers if size changed
 *   If "clear" is TRUE: clear screen if it has been resized.
 *		Returns TRUE if there is a valid screen to write to.
 *		Returns FALSE when starting up and screen not initialized yet.
 */
	int
screen_valid(clear)
	int		clear;
{
	screenalloc(clear);		/* allocate screen buffers if size changed */
	return (NextScreen != NULL);
}

#ifdef USE_MOUSE
/*
 * Move the cursor to the specified row and column on the screen.
 * Change current window if neccesary.  Returns an integer with the
 * CURSOR_MOVED bit set if the cursor has moved or unset otherwise.
 *
 * If flags has MOUSE_FOCUS, then the current window will not be changed, and
 * if the mouse is outside the window then the text will scroll, or if the
 * mouse was previously on a status line, then the status line may be dragged.
 *
 * If flags has MOUSE_MAY_VIS, then VIsual mode will be started before the
 * cursor is moved unless the cursor was on a status line.  Ignoring the
 * CURSOR_MOVED bit, this function returns one of IN_UNKNOWN, IN_BUFFER, or
 * IN_STATUS_LINE depending on where the cursor was clicked.
 *
 * If flags has MOUSE_DID_MOVE, nothing is done if the mouse didn't move since
 * the last call.
 *
 * If flags has MOUSE_SETPOS, nothing is done, only the current position is
 * remembered.
 */
	int
jump_to_mouse(flags, inclusive)
	int		flags;
	int		*inclusive;			/* used for inclusive operator, can be NULL */
{
	static int on_status_line = 0;		/* #lines below bottom of window */
	static int prev_row = -1;
	static int prev_col = -1;

	WIN		*wp, *old_curwin;
	FPOS	old_cursor;
	int		count;
	int		first;
	int		row = mouse_row;
	int		col = mouse_col;

	mouse_past_bottom = FALSE;
	mouse_past_eol = FALSE;

	if ((flags & MOUSE_DID_MOVE) && prev_row == mouse_row &&
														prev_col == mouse_col)
		return IN_BUFFER;				/* mouse pointer didn't move */

	prev_row = mouse_row;
	prev_col = mouse_col;

	if ((flags & MOUSE_SETPOS))
		return IN_BUFFER;				/* mouse pointer didn't move */

	old_curwin = curwin;
	old_cursor = curwin->w_cursor;

	if (!(flags & MOUSE_FOCUS))
	{
		if (row < 0 || col < 0)		/* check if it makes sense */
			return IN_UNKNOWN;

		/* find the window where the row is in */
		for (wp = firstwin; wp->w_next; wp = wp->w_next)
			if (row < wp->w_next->w_winpos)
				break;
		/*
		 * winpos and height may change in win_enter()!
		 */
		row -= wp->w_winpos;
		if (row >= wp->w_height)	/* In (or below) status line */
			on_status_line = row - wp->w_height + 1;
		else
			on_status_line = 0;
		win_enter(wp, TRUE);		/* can make wp invalid! */
		if (on_status_line)			/* In (or below) status line */
		{
			/* Don't use start_arrow() if we're in the same window */
			if (curwin == old_curwin)
				return IN_STATUS_LINE;
			else
				return IN_STATUS_LINE | CURSOR_MOVED;
		}

		curwin->w_cursor.lnum = curwin->w_topline;
	}
	else if (on_status_line)
	{
		/* Drag the status line */
		count = row - curwin->w_winpos - curwin->w_height + 1 - on_status_line;
		win_drag_status_line(count);
		return IN_STATUS_LINE;		/* Cursor didn't move */
	}
	else /* keep_window_focus must be TRUE */
	{
		row -= curwin->w_winpos;

		/*
		 * When clicking beyond the end of the window, scroll the screen.
		 * Scroll by however many rows outside the window we are.
		 */
		if (row < 0)
		{
			count = 0;
			for (first = TRUE; curwin->w_topline > 1; --curwin->w_topline)
			{
				count += plines(curwin->w_topline - 1);
				if (!first && count > -row)
					break;
				first = FALSE;
			}
			curwin->w_valid &=
					  ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
			redraw_later(VALID);
			row = 0;
		}
		else if (row >= curwin->w_height)
		{
			count = 0;
			for (first = TRUE; curwin->w_topline < curbuf->b_ml.ml_line_count;
														++curwin->w_topline)
			{
				count += plines(curwin->w_topline);
				if (!first && count > row - curwin->w_height + 1)
					break;
				first = FALSE;
			}
			redraw_later(VALID);
			curwin->w_valid &=
					  ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
			row = curwin->w_height - 1;
		}
		curwin->w_cursor.lnum = curwin->w_topline;
	}

#ifdef RIGHTLEFT
	if (curwin->w_p_rl)
		col = Columns - 1 - col;
#endif

	if (curwin->w_p_wrap)		/* lines wrap */
	{
		while (row)
		{
			count = plines(curwin->w_cursor.lnum);
			if (count > row)
			{
				col += row * Columns;
				break;
			}
			if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count)
			{
				mouse_past_bottom = TRUE;
				break;
			}
			row -= count;
			++curwin->w_cursor.lnum;
		}
	}
	else								/* lines don't wrap */
	{
		curwin->w_cursor.lnum += row;
		if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
		{
			curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
			mouse_past_bottom = TRUE;
		}
		col += curwin->w_leftcol;
	}

	if (curwin->w_p_nu)					/* skip number in front of the line */
		if ((col -= 8) < 0)
			col = 0;

	curwin->w_curswant = col;
	curwin->w_set_curswant = FALSE;		/* May still have been TRUE */
	if (coladvance(col) == FAIL)		/* Mouse click beyond end of line */
	{
		if (inclusive != NULL)
			*inclusive = TRUE;
		mouse_past_eol = TRUE;
	}
	else if (inclusive != NULL)
		*inclusive = FALSE;

	if ((flags & MOUSE_MAY_VIS) && !VIsual_active)
	{
		check_visual_highlight();
		VIsual = old_cursor;
		VIsual_active = TRUE;
#ifdef USE_MOUSE
		setmouse();
#endif
		if (p_smd)
			redraw_cmdline = TRUE;		/* show visual mode later */
	}

	if (curwin == old_curwin && curwin->w_cursor.lnum == old_cursor.lnum &&
									   curwin->w_cursor.col == old_cursor.col)
		return IN_BUFFER;				/* Cursor has not moved */
	return IN_BUFFER | CURSOR_MOVED;	/* Cursor has moved */
}
#endif /* USE_MOUSE */

/*
 * Return TRUE if redrawing should currently be done.
 */
	int
redrawing()
{
	return (!RedrawingDisabled && !(p_lz && char_avail() && !KeyTyped));
}

/*
 * Return TRUE if printing messages should currently be done.
 */
	int
messaging()
{
	return (!(p_lz && char_avail() && !KeyTyped));
}

/*
 * move screen 'count' pages up or down and update screen
 *
 * return FAIL for failure, OK otherwise
 */
	int
onepage(dir, count)
	int		dir;
	long	count;
{
	linenr_t		lp;
	long			n;
	int				off;
	int				retval = OK;

	if (curbuf->b_ml.ml_line_count == 1)	/* nothing to do */
	{
		beep_flush();
		return FAIL;
	}

	for ( ; count > 0; --count)
	{
		validate_botline();
		/*
		 * It's an error to move a page up when the first line is already on
		 * the screen.  It's an error to move a page down when the last line
		 * is on the screen and the topline is 'scrolloff' lines from the
		 * last line.
		 */
		if (dir == FORWARD
				? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so) &&
						curwin->w_botline > curbuf->b_ml.ml_line_count)
				: (curwin->w_topline == 1))
		{
			beep_flush();
			retval = FAIL;
			break;
		}
		if (dir == FORWARD)
		{
										/* at end of file */
			if (curwin->w_botline > curbuf->b_ml.ml_line_count)
			{
				curwin->w_topline = curbuf->b_ml.ml_line_count;
				curwin->w_valid &= ~(VALID_WROW|VALID_CROW);
			}
			else
			{
				/*
				 * When there are three or less lines on the screen, move them
				 * all to above the screen.
				 */
				if (curwin->w_botline - curwin->w_topline <= 3)
					off = 0;
				/*
				 * Make sure at least w_botline gets onto the screen, also
				 * when 'scrolloff' is non-zero and with very long lines.
				 */
				else if (plines(curwin->w_botline) +
						plines(curwin->w_botline - 1) +
						plines(curwin->w_botline - 2) >= curwin->w_height - 2)
					off = 0;
				else
					off = 2;
				curwin->w_topline = curwin->w_botline - off;
				curwin->w_cursor.lnum = curwin->w_topline;
				curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|VALID_WROW|
								   VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
			}
		}
		else	/* dir == BACKWARDS */
		{
			lp = curwin->w_topline;
			/*
			 * If the first two lines on the screen are not too big, we keep
			 * them on the screen.
			 */
			if ((n = plines(lp)) > curwin->w_height / 2)
				--lp;
			else if (lp < curbuf->b_ml.ml_line_count &&
									n + plines(lp + 1) < curwin->w_height / 2)
				++lp;
			curwin->w_cursor.lnum = lp;
			n = 0;
			while (n <= curwin->w_height && lp >= 1)
			{
				n += plines(lp);
				--lp;
			}
			if (n <= curwin->w_height)				/* at begin of file */
			{
				curwin->w_topline = 1;
				curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
			}
			else if (lp >= curwin->w_topline - 2)	/* very long lines */
			{
				--curwin->w_topline;
				comp_botline();
				curwin->w_cursor.lnum = curwin->w_botline - 1;
				curwin->w_valid &= ~(VALID_WCOL|VALID_CHEIGHT|
													   VALID_WROW|VALID_CROW);
			}
			else
			{
				curwin->w_topline = lp + 2;
				curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
			}
		}
	}
	cursor_correct();
	beginline(MAYBE);
	curwin->w_valid &= ~(VALID_WCOL|VALID_WROW|VALID_VIRTCOL);

	/*
	 * Avoid the screen jumping up and down when 'scrolloff' is non-zero.
	 */
	if (dir == FORWARD && curwin->w_cursor.lnum < curwin->w_topline + p_so)
		scroll_cursor_top(1, FALSE);

	update_screen(VALID);
	return retval;
}

/* #define KEEP_SCREEN_LINE */

	void
halfpage(flag, Prenum)
	int			flag;
	linenr_t	Prenum;
{
	long		scrolled = 0;
	int			i;
	int			n;
	int			room;

	if (Prenum)
		curwin->w_p_scroll = (Prenum > curwin->w_height) ?
												curwin->w_height : Prenum;
	n = (curwin->w_p_scroll <= curwin->w_height) ?
									curwin->w_p_scroll : curwin->w_height;

	validate_botline();
	room = curwin->w_empty_rows;
	if (flag)		/* scroll down */
	{
		while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count)
		{
			i = plines(curwin->w_topline);
			n -= i;
			if (n < 0 && scrolled)
				break;
			++curwin->w_topline;
			curwin->w_valid &= ~(VALID_CROW|VALID_WROW);
			scrolled += i;

			/*
			 * Correct w_botline for changed w_topline.
			 */
			room += i;
			do
			{
				i = plines(curwin->w_botline);
				if (i > room)
					break;
				++curwin->w_botline;
				room -= i;
			} while (curwin->w_botline <= curbuf->b_ml.ml_line_count);

#ifndef KEEP_SCREEN_LINE
			if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count)
			{
				++curwin->w_cursor.lnum;
				curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
			}
#endif
		}

#ifndef KEEP_SCREEN_LINE
		/*
		 * When hit bottom of the file: move cursor down.
		 */
		if (n > 0)
		{
			curwin->w_cursor.lnum += n;
			if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
				curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
		}
#else
			/* try to put the cursor in the same screen line */
		while ((curwin->w_cursor.lnum < curwin->w_topline || scrolled > 0)
							 && curwin->w_cursor.lnum < curwin->w_botline - 1)
		{
			scrolled -= plines(curwin->w_cursor.lnum);
			if (scrolled < 0 && curwin->w_cursor.lnum >= curwin->w_topline)
				break;
			++curwin->w_cursor.lnum;
		}
#endif
	}
	else			/* scroll up */
	{
		while (n > 0 && curwin->w_topline > 1)
		{
			i = plines(curwin->w_topline - 1);
			n -= i;
			if (n < 0 && scrolled)
				break;
			scrolled += i;
			--curwin->w_topline;
			curwin->w_valid &= ~(VALID_CROW|VALID_WROW|
											  VALID_BOTLINE|VALID_BOTLINE_AP);
#ifndef KEEP_SCREEN_LINE
			if (curwin->w_cursor.lnum > 1)
			{
				--curwin->w_cursor.lnum;
				curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
			}
#endif
		}
#ifndef KEEP_SCREEN_LINE
		/*
		 * When hit top of the file: move cursor up.
		 */
		if (n > 0)
		{
			if (curwin->w_cursor.lnum > (linenr_t)n)
				curwin->w_cursor.lnum -= n;
			else
				curwin->w_cursor.lnum = 1;
		}
#else
			/* try to put the cursor in the same screen line */
		scrolled += n;		/* move cursor when topline is 1 */
		while (curwin->w_cursor.lnum > curwin->w_topline &&
				 (scrolled > 0 || curwin->w_cursor.lnum >= curwin->w_botline))
		{
			scrolled -= plines(curwin->w_cursor.lnum - 1);
			if (scrolled < 0 && curwin->w_cursor.lnum < curwin->w_botline)
				break;
			--curwin->w_cursor.lnum;
		}
#endif
	}
	cursor_correct();
	beginline(MAYBE);
	update_screen(VALID);
}

/*
 * Give an introductory message about Vim.
 * Only used when starting Vim on an empty file, without a filename.
 */
	static void
intro_message()
{
	int			i;
	int			row;
	int			col;
	static char	*(lines[]) =
	{
		"VIM - Vi IMproved",
		"",
		"version ",
		"by Bram Moolenaar",
		"",
		"type  :help<Enter>  or  <F1>  for on-line help",
		"",
		"Vim is freely distributable",
		"type  :help uganda<Enter>  if you like Vim",
		"",
		"type  :q<Enter>  to exit"
	};

	row = (Rows - sizeof(lines) / sizeof(char *)) / 2;
	if (row > 2)
	{
		for (i = 0; i < (int)(sizeof(lines) / sizeof(char *)); ++i)
		{
			col = strlen(lines[i]);
			if (i == 2)
				col += strlen(mediumVersion);
			col = (Columns - col) / 2;
			if (col < 0)
				col = 0;
			screen_puts((char_u *)lines[i], row, col, 0);
			if (i == 2)
				screen_puts((char_u *)mediumVersion, row, col + 8, 0);
			++row;
		}
	}
}

	void
do_intro()
{
	screenclear();
	intro_message();
	wait_return(TRUE);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.