[alt.sources] STDWIN 0.9.5, Part 04/19

guido@cwi.nl (Guido van Rossum) (03/04/91)

Archive-name: stdwin/part04

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 4 (of 19)."
# Contents:  Appls/test/test1.c Packs/vt/vt.c Ports/x11/window.c
# Wrapped by guido@voorn.cwi.nl on Mon Mar  4 12:37:24 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Appls/test/test1.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Appls/test/test1.c'\"
else
echo shar: Extracting \"'Appls/test/test1.c'\" \(251 characters\)
sed "s/^X//" >'Appls/test/test1.c' <<'END_OF_FILE'
X/* Basic test -- open and close a window; no output or events. */
X
X#include "stdwin.h"
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	WINDOW *win;
X	winitargs(&argc, &argv);
X	win= wopen("Basic test", (void (*)()) 0);
X	wclose(win);
X	wdone();
X	exit(0);
X}
END_OF_FILE
if test 251 -ne `wc -c <'Appls/test/test1.c'`; then
    echo shar: \"'Appls/test/test1.c'\" unpacked with wrong size!
fi
# end of 'Appls/test/test1.c'
fi
if test -f 'Packs/vt/vt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Packs/vt/vt.c'\"
else
echo shar: Extracting \"'Packs/vt/vt.c'\" \(20134 characters\)
sed "s/^X//" >'Packs/vt/vt.c' <<'END_OF_FILE'
X/* Virtual Terminal -- basic output routines */
X
X#include "vtimpl.h"
X
X#ifdef macintosh
X/* Bold mode uses the "Bold" font, found in VersaTerm;
X   this matches Monaco and is available only in 9 pt. */
X#define DO_BOLD
X#define SETBOLD() wsetfont("Bold")
X#define SETNORMAL() wsetfont("Monaco")
X#endif
X
X/* Forward */
XSTATIC void vtdrawproc _ARGS((WINDOW *win,
X				int left, int top, int right, int bottom));
XSTATIC void vtdraw _ARGS((VT *vt, int r1, int c1, int r2, int c2));
XSTATIC void vtchrange _ARGS((VT *vt, int r1, int c1, int r2, int c2));
X
X/* Administration for vtfind */
X
Xstatic VT **vtlist;	/* Registered VT structs */
Xstatic int nvt;		/* Number of registered VT structs */
X
X/* Open a VT window of given size */
X
XVT *
Xvtopen(title, rows, cols, save)
X	char *title;
X	int rows, cols;
X	int save; /* Number of rows scroll-back capacity */
X{
X	int row;
X	VT *vt = ALLOC(VT);
X
X	if (vt == NULL)
X		return NULL;
X	rows += save; /* Documentsize: cols * (rows + scrollback) */
X	vt->rows = rows;
X	vt->cols = cols;
X	vt->topterm = save;
X	vt->cwidth = wcharwidth('0');
X	vt->cheight = wlineheight();
X
X	vt->llen = NALLOC(short, rows);
X	vt->data = NALLOC(char *, rows);
X	vt->flags = NALLOC(unsigned char *, rows);
X	for (row = 0; row < rows; ++row) {
X		if (vt->data != NULL)
X			vt->data[row] = NALLOC(char, cols);
X		if (vt->flags != NULL)
X			vt->flags[row] = NALLOC(unsigned char, cols);
X	}
X	/* Assume that if one NALLOC fails, all following ones
X	   for the same size also fail... */
X	if (vt->llen == NULL ||
X		vt->data == NULL ||
X		vt->data[rows-1] == NULL ||
X		vt->flags == NULL ||
X		vt->flags[rows-1] == NULL) {
X		vt->win = NULL;
X		vtclose(vt);
X		return NULL;
X	}
X
X	vt->win = wopen(title, vtdrawproc);
X	if (vt->win == NULL) {
X		vtclose(vt);
X		return NULL;
X	}
X	
X	wsetdocsize(vt->win, cols * vt->cwidth, rows * vt->cheight);
X	wsetwincursor(vt->win, wfetchcursor("ibeam"));
X
X	vt->nlcr = FALSE;
X	vt->drawing = FALSE;
X	vtreset(vt); /* Clear additional fields */
X	L_APPEND(nvt, vtlist, VT *, vt);
X
X	return vt;
X}
X
X/* Close a VT window.
X   Also used to clean-up when vtopen failed half-way */
X
Xvoid
Xvtclose(vt)
X	VT *vt;
X{
X	int i, row;
X
X	for (i = 0; i < nvt; ++i) {
X		if (vt == vtlist[i]) {
X			L_REMOVE(nvt, vtlist, VT *, i);
X			break;
X		}
X	}
X
X	if (vt->win != NULL)
X		wclose(vt->win);
X	for (row = 0; row < vt->rows; ++row) {
X		if (vt->data != NULL)
X			FREE(vt->data[row]);
X		if (vt->flags != NULL)
X			FREE(vt->flags[row]);
X	}
X	FREE(vt->data);
X	FREE(vt->flags);
X	FREE(vt);
X}
X
X/* Output a string to a VT window.
X   This does not do escape sequence parsing or control character
X   interpretation, but honors the 'modes' the VT can be in
X   (insert, inverse/underline etc.) and the scrolling region.
X   It updates the cursor position and scrolls if necessary.
X   Note that after one or more calls to vtputstring it is necessary
X   to call vtsync(vt)! */
X
Xvoid
Xvtputstring(vt, text, len)
X	VT *vt;
X	char *text;
X	int len;
X{
X	int row = vt->cur_row;
X	int col = vt->cur_col;
X	short *llen = vt->llen;
X	int scr_top = (vt->scr_top == vt->topterm) ? 0 : vt->scr_top;
X	int scr_bot = (vt->cur_row >= vt->scr_bot) ? vt->rows : vt->scr_bot;
X	int wrap = 0; /* Number of times wrapped around */
X	int last_drawn_col;
X
X#ifndef NDEBUG
X	if (scr_top < 0 || scr_top > vt->rows) {
X		fprintf(stderr, "vtputstring: scr_top = %d\n", scr_top);
X		vtpanic("vtputstring bad scr_top");
X	}
X
X	if (scr_bot < 0 || scr_bot > vt->rows) {
X		fprintf(stderr, "vtputstring: scr_bot = %d\n", scr_bot);
X		vtpanic("vtputstring bad scr_bot");
X	}
X#endif
X
X	/* Compute text length if necessary */
X	if (len < 0)
X		len = strlen(text);
X
X	/* This is a 'do' loop so that a call with an empty
X	   string will still normalize the cursor position */
X
X	/* XXX This loop is 100 lines long!  Sorry. */
X	
X	do {
X		int llen_row, n, oldcol;
X
X		/* Normalize the cursor position.
X		   When we get past the bottom edge we must scroll,
X		   but the actual scrolling is delayed until later:
X		   here we just wrap around and remember how many
X		   times we've wrapped.
X		   Thus, scrolling multiple lines is effected as
X		   a 'jump' scroll up -- not so nice-looking,
X		   but essential with current performance limitations
X		   of bitblit hardware.
X		   When faster machines become available we may need
X		   an option to turn this optimization off in favour
X		   of smooth scrolling. */
X
X		if (col >= vt->cols) {
X			col = 0;
X			++row;
X			if (wrap > 0 && row < scr_bot)
X				llen[row] = 0;	/* Clear line	*/
X		}
X		if (row >= scr_bot) {
X			/* Should be able to turn this off? */
X			if (wrap == 0) last_drawn_col = col;
X			++wrap;
X			row = scr_top;
X			llen[row] = 0;
X		}
X		oldcol = col; /* For vtdraw call below */
X
X		/* If the cursor is beyond the current line length,
X		   eg because of cursor positioning, pad with space.
X		   Set llen_row to the new line length. */
X		llen_row = llen[row];
X		if (llen_row < col) {
X			do {
X				vt->data[row][llen_row] = ' ';
X				vt->flags[row][llen_row] = 0;
X			} while (++llen_row < col);
X		}
X
X		/* Set n to the number of characters that can be
X		   inserted in the current line */
X		n = vt->cols - col;
X		CLIPMAX(n, len);
X
X		/* When inserting, shift the rest of the line right.
X		   The last characters may fall of the edge. */
X		if (vt->insert && llen_row > col && col+n < vt->cols) {
X			int k;
X			llen_row += n;
X			CLIPMAX(llen_row, vt->cols);
X			vtscroll(vt, row, col, row+1, llen_row, 0, n);
X			for (k = llen_row - n; --k >= col; ) {
X				vt->data[row][k+n] = vt->data[row][k];
X				vt->flags[row][k+n] = vt->flags[row][k];
X			}
X		}
X
X		/* Copy the characters into the line data */
X#ifndef NDEBUG
X		if (col + n > vt->cols || row >= vt->rows) {
X			fprintf(stderr, "col=%d, n=%d, row=%d\n", col,n,row);
X			vtpanic("Bad index in vtputstring");
X		}
X#endif
X		strncpy(vt->data[row] + col, text, n);
X
X		/* Update loop administration, maintaining the invariant:
X		   'len' characters starting at 'text' still to do */
X		len -= n;
X		text += n;
X
X		/* Set the corresponding flag bits.
X		   The current column is set as a side effect. */
X		while (--n >= 0)
X			vt->flags[row][col++] = vt->gflags;
X
X		/* Update line length */
X		CLIPMIN(llen_row, col);
X		llen[row] = llen_row;
X
X		/* Maybe draw the characters now */
X		if (vt->lazy) {
X			D( printf("vtputstring: ") );
X			vtchrange(vt, row, oldcol, row, col);
X		}
X		else if (wrap == 0) {
X			VTBEGINDRAWING(vt);
X			werase(oldcol*vt->cwidth, row*vt->cheight,
X			    col*vt->cwidth, (row+1)*vt->cheight);
X
X			D( printf("vtputstring: ") );
X			last_drawn_col = col;
X			vtdraw(vt, row, oldcol, row + 1, col);
X		}
X
X		/* Loop while more work to do */
X
X	} while (len > 0);
X
X#if 0
X	/* XXX Why not? */
X	vtsetcursor(vt, row, col);
X#endif
X
X	/* Process delayed scrolling. */
X
X	if (wrap > 0) {
X		/* Yes, we need to scroll.
X		   When wrap > 1, we have scrolled more than a screenful;
X		   then the vtscroll call is skipped, but we must still
X		   circulate the lines internally. */
X
X		/* Picture:
X
X		   scr_top ___________________ (first line affected)
X		      .    ___________________
X		      .    ______ row ________
X		      .    ___________________ (last line affected)
X		   scr_bot
X
X		   Data move: circulate down so that the data at row
X		   moves to scr_bot - 1.
X		   Screen bits move: the line below row must
X		   become scr_top.
X		   We must also invalidate the characters changed
X		   before we started scrolling, but we must do this
X		   after the vtscroll call, because some STDWIN
X		   versions don't properly scroll invalidated bits.
X		*/
X
X		if (row + 1 != scr_bot) vtcirculate(vt,
X					scr_top, scr_bot,
X					scr_bot - (row + 1));
X		if (wrap == 1) {
X			int n = (row + 1) - scr_top;
X			D( printf("Wrapped once; n=%d\n", n) );
X			vtscroll(vt, scr_top, 0, scr_bot, vt->cols, -n, 0);
X			if (!vt->lazy)
X				vtdraw(vt, scr_bot - n, last_drawn_col,
X					scr_bot, vt->cols);
X		}
X		else { /* Scrolled more than the scrolling region */
X			D( printf("Whole scrolling region: ") );
X			if (!vt->lazy)
X				vtdraw(vt, scr_top, 0, scr_bot, vt->cols);
X		}
X		row = scr_bot - 1;
X	}
X
X	/* Set the new cursor position */
X	vtsetcursor(vt, row, col);
X}
X
X/* Subroutine to invalidate the text range from (r1, c1) to (r2, c2).
X   This is not the same as vtchange; this function deals with text
X   ranges, while vtchange deals with rectangles. */
X
XSTATIC void
Xvtchrange(vt, r1, c1, r2, c2)
X	VT *vt;
X{
X	D( printf("vtchrange [%d,%d]~[%d,%d]\n", c1, r1, c2, r2) );
X	if (c1 >= vt->cols) {
X		MON_EVENT("vtchrange: c1 >= vt->cols");
X		++r1; c1 = 0;
X	}
X	if (r1 >= r2) {
X		vtchange(vt, r1, c1, r2+1, c2);
X	}
X	else {
X		vtchange(vt, r1, c1, r1+1, vt->cols);
X		vtchange(vt, r1+1, 0, r2, vt->cols);
X		vtchange(vt, r2, 0, r2+1, c2);
X	}
X}
X
X/* Set cursor position.
X   This sets the STDWIN text caret and calls wshow for the character
X   at the cursor.
X   The cursor position is clipped to the screen dimensions,
X   but it may sit on the right edge just beyond the last character. */
X
Xvoid
Xvtsetcursor(vt, row, col)
X	VT *vt;
X	int row, col;
X{
X	CLIPMAX(row, vt->rows - 1);
X	CLIPMIN(row, 0);
X	CLIPMAX(col, vt->cols);
X	CLIPMIN(col, 0);
X	vt->cur_row = row;
X	vt->cur_col = col;
X	CLIPMAX(col, vt->cols - 1);
X	if (!vt->lazy) {
X		VTENDDRAWING(vt);
X		wsetcaret(vt->win, col * vt->cwidth, row * vt->cheight);
X		vtshow(vt, row, col, row + 1, col);
X	}
X}
X
X/* Set scrolling region.  Lines in [top...bot) can scroll.
X   If the parameters are valid, set the region and move to (0, 0);
X   if there is an error, reset the region and don't move.
X   (NB: the move is to (0, 0), not to the top of the region!) */
X
Xvoid
Xvtsetscroll(vt, top, bot)
X	VT *vt;
X	int top, bot;
X{
X	vtsync(vt);
X	if (top >= vt->topterm && top < bot && bot <= vt->rows) {
X		vt->scr_top = top;
X		vt->scr_bot = bot;
X		vtsetcursor(vt, vt->topterm, 0);
X		/* vtshow(vt, vt->topterm, 0, vt->rows, vt->cols); */
X	}
X	else {
X		vt->scr_top = vt->topterm;
X		vt->scr_bot = vt->rows;
X	}
X	vtshow(vt, vt->scr_top, 0, vt->scr_bot, vt->cols);
X}
X
X/* Major reset */
X
Xvoid
Xvtreset(vt)
X	VT *vt;
X{
X	int row;
X
X	vtchange(vt, 0, 0, vt->rows, vt->cols);
X	vtshow(vt, vt->topterm, 0, vt->rows, vt->cols);
X
X	for (row = 0; row < vt->rows; ++row)
X		vt->llen[row] = 0;
X
X	vt->toscroll = 0;
X	vtsetflags(vt, 0);
X	vtsetinsert(vt, FALSE);
X	vtsetscroll(vt, 0, 0);
X	vtsetcursor(vt, vt->topterm, 0);
X	vt->sel_col1 = vt->sel_row1 = 0;
X	vt->sel_col2 = vt->sel_row2 = 0;
X	vt->save_row = vt->save_col = 0;
X	vt->keypadmode = FALSE;
X	vt->lazy = FALSE;
X	vt->mitmouse = FALSE;
X	vt->visualbell = FALSE;
X	vt->flagschanged = TRUE;
X	vt->action = NULL; /* This invalidates all other parsing fields */
X}
X
X/* Draw procedure - this one is called because stdwin discovered an expose */
X
XSTATIC void
Xvtdrawproc(win, left, top, right, bottom)
X	WINDOW *win;
X	int left, top, right, bottom;
X{
X	VT *vt = vtfind(win);
X
X#ifndef NDEBUG
X	if (vt == NULL) vtpanic("vtdrawproc not for VT window");
X	if (vt->drawing) vtpanic("vtdrawproc while drawing");
X#endif
X
X	vt->drawing = 1;	/* Stdwin did this implicitely */
X	{
X		int cw = vt->cwidth;
X		int ch = vt->cheight;
X		int col1 = left / cw;
X		int col2 = (right + cw - 1) / cw;
X		int row1 = top / ch;
X		int row2 = (bottom + ch - 1) / vt->cheight;
X
X		D( printf("vtdrawproc: ") );
X		vtdraw(vt, row1, col1, row2, col2);
X	}
X	vt->drawing = 0;	/* Stdwin will do this implicitely */
X}
X
XSTATIC void
Xset_textstyle(vt, flags)
X    VT *vt;
X    int flags;
X{
X#if 0
X    /* This isn't right, for various reasons.  And do we need it? */
X    static int previous_flags = -3;	/* Or anything < 0	*/
X
X    if (flags == previous_flags) return;
X    D( printf("Set_textstyle: 0x%x => 0x%x\n", previous_flags, flags) );
X    previous_flags = flags;
X#endif
X    wsetplain();
X    if (flags & VT_UNDERLINE) wsetunderline();
X    if (flags & VT_INVERSE) wsetinverse();
X#ifdef DO_BOLD
X    if (flags & VT_BOLD) SETBOLD();
X    else SETNORMAL();
X#endif
X}
X
X/* Draw procedure - doesn't draw [row2, col2], or any other
X   char at row2, takes care of underlining, inverse video etc */
X
XSTATIC void
Xvtdraw(vt, row1, col1, row2, col2)
X    VT *vt;
X    int row1, col1, row2, col2;
X{
X    int cw = vt->cwidth;
X    int ch = vt->cheight;
X    register unsigned char cur_flags;
X    int row;
X
X    VTBEGINDRAWING(vt);
X    D( printf("vtdraw [%d,%d]~[%d,%d]\n", col1, row1, col2, row2) );
X
X    CLIPMIN(col1, 0);
X    CLIPMAX(col2, vt->cols);
X    CLIPMIN(row1, 0);
X    CLIPMAX(row2, vt->rows);
X
X    for (row = row1; row < row2; ++row) {
X	register int col;
X	char *data_row = vt->data[row] + col1;
X	register unsigned char *flags_row = vt->flags[row] + col1;
X	int h = col1*cw;
X	int v = row*ch;
X	int first = col1;
X	register int last = vt->llen[row];
X
X	CLIPMAX(last, col2);	/* Don't draw more than asked for */
X
X	/* Set flags */
X	cur_flags = flags_row[0];
X	set_textstyle(vt, cur_flags);
X
X	/* Attempt to draw as much text as possible in one wdrawtext */
X	for (col = first; col < last; ++col) {
X	    if (*flags_row++ != cur_flags) {
X		int n = col-first;
X		/* n cannot be < 0; ==0 means this
X		   line has different flags */
X		if (n > 0) {
X		    wdrawtext(h, v, data_row, n);
X		    first = col;
X		    data_row += n;
X		    h += n*cw;
X		}
X
X		cur_flags = flags_row[-1];	/* Set new flags */
X		set_textstyle(vt, cur_flags);
X	    }
X	}
X	/* Draw leftover text on this line and perhaps some black spaces: */
X	if (col > first) wdrawtext(h, v, data_row, col-first);
X    }
X}
X
X/* Find the VT corresponding to a WINDOW */
X
XVT *
Xvtfind(win)
X	WINDOW *win;
X{
X	int i;
X	for (i = 0; i < nvt; ++i) {
X		if (vtlist[i]->win == win)
X			return vtlist[i];
X	}
X	return NULL;
X}
X
X/* Subroutine to circulate lines.
X   For i in r1 ... r2-1, move line i to position i+n (modulo r2-r1).
X
X   For ABS(n)==1, we have a fast solution that always works.
X   For larger n, we have a slower solution allocating a temporary buffer;
X   if we can't, we repeat the fast solution ABS(n) times (really slow).
X
X   We assume reasonable input:
X	0 <= r1 < r2 <= vt->rows,
X	0 < abs(n) < r2-r1.
X*/
X
Xvoid
Xvtcirculate(vt, r1, r2, n)
X	register VT *vt;
X	int r1, r2;
X	int n;
X{
X	if (n == -1) { /* Fast solution, move 1 up */
X		char *tdata = vt->data[r1];
X		unsigned char *tflags = vt->flags[r1];
X		short tllen = vt->llen[r1];
X		register int i;
X		MON_EVENT("circulate -1");
X		for (i = r1+1; i < r2; ++i) {
X			vt->data[i-1] = vt->data[i];
X			vt->flags[i-1] = vt->flags[i];
X			vt->llen[i-1] = vt->llen[i];
X		}
X		vt->data[i-1] = tdata;
X		vt->flags[i-1] = tflags;
X		vt->llen[i-1] = tllen;
X	}
X	else if (n == 1) { /* Fast solution, move 1 down */
X		char *tdata = vt->data[r2-1];
X		unsigned char *tflags = vt->flags[r2-1];
X		short tllen = vt->llen[r2-1];
X		register int i;
X		MON_EVENT("circulate 1");
X		for (i = r2-1; i > r1; --i) {
X			vt->data[i] = vt->data[i-1];
X			vt->flags[i] = vt->flags[i-1];
X			vt->llen[i] = vt->llen[i-1];
X		}
X		vt->data[i] = tdata;
X		vt->flags[i] = tflags;
X		vt->llen[i] = tllen;
X	}
X	else if (n != 0) {
X		if (!slowcirculate(vt, r1, r2, n)) {
X			/* Couldn't -- do ABS(n) times the fast case... */
X			int step;
X			if (n < 0) {
X				n = -n;
X				step = -1;
X			}
X			else step = 1;
X			while (--n >= 0)
X				vtcirculate(vt, r1, r2, step);
X		}
X	}
X}
X
X/* Slow version of the above; move lines r1..r2-1 n lines up */
X
XSTATIC bool
Xslowcirculate(vt, r1, r2, n)
X	register VT *vt;
X	int r1, r2;
X	int n; /* May be negative */
X{
X	char **tdata; /* Data buffer */
X	unsigned char **tflags; /* Flags buffer */
X	short *tllen; /* Line length buffer */
X	bool ok;
X
X	if (n < 0) n += (r2 - r1);
X	tdata = NALLOC(char *, n);
X	tflags = NALLOC(unsigned char *, n);
X	tllen = NALLOC(short, n);
X
X	MON_EVENT("slowcirculate");
X	/* Did all the malloc's work? */
X	ok = tdata != NULL && tflags != NULL && tllen != NULL;
X	if (ok) {
X		register int i;
X		r2 -= n; /* Now r2 "points" beyond the last target line */
X		/* Save data, flags and lengths to be overwritten */
X		for (i = 0; i < n; ++i) {
X			tdata[i] = vt->data[r2+i];
X			tflags[i] = vt->flags[r2+i];
X			tllen[i] = vt->llen[r2+i];
X		}
X		/* Copy "lower" part of the lines to r1..r1+n-1 (=r2-1) */
X		for (i = r2; --i >= r1; ) {
X			vt->data[i+n] = vt->data[i];
X			vt->flags[i+n] = vt->flags[i];
X			vt->llen[i+n] = vt->llen[i];
X		}
X		/* Restore saved lines in r1..r1+n-1 */
X		for (i = 0; i < n; ++i) {
X			vt->data[r1+i] = tdata[i];
X			vt->flags[r1+i] = tflags[i];
X			vt->llen[r1+i] = tllen[i];
X		}
X	}
X
X	FREE(tdata);
X	FREE(tflags);
X	FREE(tllen);
X
X	return ok;
X}
X
X/* VT interface to wchange */
X
Xvoid
Xvtchange(vt, r1, c1, r2, c2)
X	VT *vt;
X	int r1, c1, r2, c2;
X{
X	VTENDDRAWING(vt);
X	D( printf("vtchange [%d, %d]~[%d, %d]\n", c1, r1, c2, r2) );
X	wchange(vt->win,
X		c1 * vt->cwidth, r1 * vt->cheight,
X		c2 * vt->cwidth, r2 * vt->cheight);
X}
X
X/* VT interface to wshow */
X
Xvoid
Xvtshow(vt, r1, c1, r2, c2)
X	VT *vt;
X	int r1, c1, r2, c2;
X{
X	VTENDDRAWING(vt);
X	wshow(vt->win,
X		c1 * vt->cwidth, r1 * vt->cheight,
X		c2 * vt->cwidth, r2 * vt->cheight);
X}
X
X/* VT interface to wscroll.
X   In lazy mode, the actual scrolling may be postponed
X   (by setting vt->toscroll). */
X
Xvoid
Xvtscroll(vt, r1, c1, r2, c2, drow, dcol)
X	VT *vt;
X	int r1, c1, r2, c2;
X	int drow, dcol; /* Translation vector */
X{
X	int scr_top = vt->scr_top;
X	if (scr_top == vt->topterm)
X		scr_top = 0;
X
X	D( printf("vtscroll %d lines\n", drow) );
X
X	if (vt->lazy && dcol == 0 && r1 == scr_top && r2 == vt->scr_bot &&
X		c1 == 0 && c2 == vt->cols) {
X		if (drow * vt->toscroll < 0)
X			vtsync(vt);
X		vt->toscroll += drow;
X	}
X	else {
X		if (vt->toscroll != 0 && r1 < vt->scr_bot && r2 > scr_top)
X			vtsync(vt); /* Execute leftover scrolling first */
X
X		/* Convert to STDWIN coordinates */
X		c1 *= vt->cwidth;
X		c2 *= vt->cwidth;
X		dcol *= vt->cwidth;
X
X		r1 *= vt->cheight;
X		r2 *= vt->cheight;
X		drow *= vt->cheight;
X
X		VTENDDRAWING(vt);
X		wnocaret(vt->win);
X		wscroll(vt->win, c1, r1, c2, r2, dcol, drow);
X
X		/* Despite what the stdwin document says,
X		   wscroll doesn't generate wchanges anymore */
X		if (vt->lazy) {
X			if (drow < 0) {		/* Scrolled upwards	*/
X				wchange(vt->win, c1, r2+drow, c2, r2);
X				D( printf("^: wchange(%d, %d, %d, %d)\n",
X						c1, r2+drow, c2, r2) );
X			}
X			else if (drow > 0) {	/* Scrolled downwards	*/
X				wchange(vt->win, c1, r1, c2, r1+drow);
X				D( printf("V: wchange(%d, %d, %d, %d)\n",
X						c1, r1, c2, r1+drow) );
X			}
X			if (dcol < 0) {		/* Scrolled to the left	*/
X				wchange(vt->win, c2+dcol, r1, c2, r2);
X				D( printf("<: wchange(%d, %d, %d, %d)\n",
X						c2+dcol, r1, c2, r2) );
X			}
X			else if (dcol > 0) {	/* Scrolled to the right */
X				wchange(vt->win, c1, r1, c1+dcol, r2);
X				D( printf(">: wchange(%d, %d, %d, %d)\n",
X						c1, r1, c1+dcol, r2) );
X			}
X		}
X	}
X}
X
X/* Execute delayed scrolling.
X   Don't call from within drawproc: wscroll while drawing is BAD. */
X
Xvoid
Xvtsync(vt)
X	VT *vt;
X{
X	VTENDDRAWING(vt);
X	if (vt->toscroll != 0) {
X		int scr_top = vt->scr_top;
X#if 0
X		/* XXX Why not? */
X		if (vt->toscroll < 0) vtpanic("vtsync: toscroll < 0");
X#endif
X		if (scr_top == vt->topterm)
X			scr_top = 0;
X		D( printf("VtSync [,%d]~[,%d] %d: wscroll\n",
X			scr_top, vt->scr_bot, vt->toscroll) );
X		wscroll(vt->win,
X			0, scr_top * vt->cheight,
X			vt->cols * vt->cwidth, vt->scr_bot * vt->cheight,
X			0, vt->toscroll * vt->cheight);
X
X		D( printf("vtsync: ") );
X		/* I could get speedup from remembering
X		 * min and max columns for the call here
X		 */
X		vtchange(vt, vt->scr_bot + vt->toscroll, 0,
X			vt->scr_bot, vt->cols);
X		vt->toscroll = 0;
X	}
X	/* vtsetcursor doesn't do this if vt->lazy: */
X	if (vt->lazy) {
X		MON_EVENT("vtsync for lazy");
X		wsetcaret(vt->win,
X			vt->cur_col * vt->cwidth,
X			vt->cur_row * vt->cheight);
X		vtshow(vt, vt->cur_row,vt->cur_col, vt->cur_row+1,vt->cur_col);
X	}
X}
X
X/* Internal VT interface to winvert.
X   Must be called between wbegindrawing and wenddrawing. */
X
Xvoid
Xvtinvert(vt, row1, col1, row2, col2)
X	VT *vt;
X	int row1, col1, row2, col2;
X{
X	/* XXX Here was some code */
X
X	if (row1 == row2) {
X		/* Whole selection within one line */
X		winvert(col1 * vt->cwidth, row1 * vt->cheight,
X			col2 * vt->cwidth, (row2 + 1) * vt->cheight);
X	}
X	else {
X		/* Invert first line of the selection */
X		winvert(col1 * vt->cwidth, row1 * vt->cheight,
X			vt->cols * vt->cwidth, (row1 + 1) * vt->cheight);
X
X		/* Invert intermediate lines, if any */
X		if (row1 + 1 < row2) {
X			winvert(0, (row1 + 1) * vt->cheight,
X				vt->cols * vt->cwidth, row2 * vt->cheight);
X		}
X
X		/* Invert last line */
X		winvert(0, row2 * vt->cheight,
X			col2 * vt->cwidth, (row2 + 1) * vt->cheight);
X	}
X}
END_OF_FILE
if test 20134 -ne `wc -c <'Packs/vt/vt.c'`; then
    echo shar: \"'Packs/vt/vt.c'\" unpacked with wrong size!
fi
# end of 'Packs/vt/vt.c'
fi
if test -f 'Ports/x11/window.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Ports/x11/window.c'\"
else
echo shar: Extracting \"'Ports/x11/window.c'\" \(30704 characters\)
sed "s/^X//" >'Ports/x11/window.c' <<'END_OF_FILE'
X/* X11 STDWIN -- Window operations */
X
X#include "x11.h"
X#include "llevent.h"
X#include <X11/Xutil.h> /* For Rectangle{In,Out,Part} */
X
X
X/* Margin size around inner window (space for scroll and menu bars) */
X
X#define LMARGIN		16
X/*
X#define TMARGIN		(MIN((_wmf->ascent + _wmf->descent), 16) + 2)
X*/
X#define TMARGIN		18
X#define RMARGIN		0
X#define BMARGIN		16
X#define IMARGIN		2
X
X
X/* Event masks */
X
X/* Mask for 'wo' */
X#define OUTER_MASK	( KeyPressMask \
X			| FocusChangeMask \
X			| EnterWindowMask \
X			| LeaveWindowMask \
X			| StructureNotifyMask \
X			)
X
X/* Mask for other windows except 'wi' */
X#define OTHER_MASK	( ButtonPressMask \
X			| ButtonReleaseMask \
X			| ButtonMotionMask \
X			| ExposureMask \
X			)
X
X	
X/* Private globals */
X
Xstatic int def_h, def_v;
Xstatic int def_width, def_height;
X
X#define DEF_WIDTH (def_width > 0 ? def_width : 80*wcharwidth('n'))
X#define DEF_HEIGHT (def_height > 0 ? def_height : 22*wlineheight())
X
X
X/* WINDOW list.
X   Each WINDOW must be registered here, so it can be found back
X   by _whichwin */
X
Xstatic WINDOW **winlist;
Xstatic int nwins;
X
X
X/* Find a WINDOW pointer, given a Window.
X   Hunt through the WINDOW list, for each element comparing the given wid
X   with the wids of all (sub)Windows */
X
XWINDOW *
X_whichwin(w)
X	register Window w;
X{
X	register int i;
X	
X	for (i= nwins; --i >= 0; ) {
X		register WINDOW *win= winlist[i];
X		register int j;
X		for (j= NSUBS; --j >= 0; ) {
X			if (w == win->subw[j].wid)
X				return win;
X		}
X	}
X	_wdebug(2, "_whichwin: can't find Window %x", w);
X	return NULL;
X}
X
X
X/* Return some window */
X
XWINDOW *
X_w_somewin()
X{
X	if (nwins <= 0)
X		return NULL;
X	return winlist[0];
X}
X
X
X/* Set the max size of windows created later (ignored for now) */
X
Xvoid
Xwsetmaxwinsize(width, height)
X	int width, height;
X{
X}
X
X
X/* Set the initial size of windows created later */
X
Xvoid
Xwsetdefwinsize(width, height)
X	int width, height;
X{
X	if (width <= 0)
X		def_width= 0;
X	else {
X		CLIPMAX(width, WidthOfScreen(_ws) - 40 - LMARGIN - RMARGIN);
X		CLIPMIN(width, 40);
X		def_width= width;
X	}
X	if (height <= 0)
X		def_height= 0;
X	else {
X		CLIPMAX(height, HeightOfScreen(_ws) - 40 - TMARGIN - BMARGIN);
X		CLIPMIN(height, 40);
X		def_height= height;
X	}
X}
X
Xvoid
Xwgetdefwinsize(pwidth, pheight)
X	int *pwidth, *pheight;
X{
X	*pheight = def_height;
X	*pwidth = def_width;
X}
X
X
X/* Set the initial position of windows created later */
X
Xvoid
Xwsetdefwinpos(h, v)
X	int h, v;
X{
X	CLIPMIN(h, 0);
X	CLIPMIN(v, 0);
X	def_h= h;
X	def_v= v;
X}
X
Xvoid
Xwgetdefwinpos(ph, pv)
X	int *ph, *pv;
X{
X	*ph = def_h;
X	*pv = def_v;
X}
X
X
X/* Read a Bitmap from a file and convert it to a Pixmap.
X   XXX Actually I don't convert it to a Pixmap; this may mean that perhaps
X   you won't be able to set an icon on a color display, depending
X   on the intelligence of the window manager.
X   XXX Note that to fix this you must create separate functions to read
X   Bitmaps and Pixmaps, as Bitmaps have a real use (for icon masks). */
X
X#define readpixmap readbitmap
X
X/* Read a bitmap from file */
X
Xstatic Pixmap
Xreadbitmap(filename)
X	char *filename;
X{
X	unsigned int width, height;
X	int xhot, yhot;
X	Pixmap bitmap;
X	int err= XReadBitmapFile(_wd, RootWindowOfScreen(_ws), filename,
X		&width, &height, &bitmap, &xhot, &yhot);
X	if (err != BitmapSuccess) {
X		_wwarning("can't read bitmap file %s, error code %d",
X			filename, err);
X		return None;
X	}
X	return bitmap;
X}
X
X
X/* Open a WINDOW.
X   Some defaults should only be used for the first window opened,
X   e.g., window geometry (otherwise all windows would overlay each other!)
X   and the "iconic" property.  Icon bitmaps will be used by all windows. */
X
XWINDOW *
Xwopen(title, drawproc)
X	char *title;
X	void (*drawproc)();
X{
X	static bool used_defaults;
X	WINDOW *win;
X	XSizeHints sizehints;
X	
X	/* Allocate zeroed storage for the WINDOW structure
X	   and fill in the easy non-zero values */
X	win= (WINDOW*) calloc(sizeof(WINDOW), 1);
X	if (win == NULL) {
X		_werror("wopen: can't alloc storage for window");
X		return NULL;
X	}
X	win->drawproc= drawproc;
X	win->careth= win->caretv= -1;
X	win->attr= wattr;
X	
X	/* Parse user-specified geometry default.
X	   This overrides what the application specified.
X	   Note that the x and y stored internally are exclusive or borders,
X	   while X geometries specify x and y including the border.
X	   XXX Also note that the obsolete sizehints members x, y, width and
X	   height are used later to actually create the window. */
X	{
X		char *geom;
X		sizehints.x= def_h <= 0 ? 0 : def_h - 2*IBORDER;
X		sizehints.y= def_v <= 0 ? 0 : def_v - 2*IBORDER;
X		sizehints.width= DEF_WIDTH + LMARGIN + RMARGIN;
X		sizehints.height= DEF_HEIGHT + TMARGIN + BMARGIN;
X		sizehints.flags= PSize;
X		if (def_h > 0 || def_v > 0)
X			sizehints.flags |= PPosition | USPosition;
X			/* USPosition added to fool twm */
X		if (!used_defaults &&
X			(geom= _wgetdefault("geometry", "Geometry")) != NULL){
X			unsigned int width, height;
X			int flags= XParseGeometry(geom,
X				&sizehints.x, &sizehints.y, &width, &height);
X			if (flags & WidthValue)
X				sizehints.width = width;
X			if (flags & HeightValue)
X				sizehints.height = height;
X			if (flags & XNegative)
X				sizehints.x=
X					WidthOfScreen(_ws) + sizehints.x
X					- sizehints.width - 2*IBORDER;
X			if (flags & YNegative)
X				sizehints.y=
X					HeightOfScreen(_ws) + sizehints.y
X					- sizehints.height - 2*IBORDER;
X			
X			/* Use the user-specified size as the default
X			   size for future windows */
X			
X			if (flags & WidthValue)
X				def_width=
X					sizehints.width - LMARGIN - RMARGIN;
X			if (flags & HeightValue)
X				def_height=
X					sizehints.height - TMARGIN - BMARGIN;
X			
X			/* If the user has given as position,
X			   pretend a size is also given, otherwise
X			   UWM will still ask for interactive
X			   window placement.  I'm in good company:
X			   "the" Toolkit also does this. */
X			
X			if (flags & (XValue|YValue))
X				sizehints.flags |= USPosition|USSize;
X			else if (flags & (WidthValue|HeightValue))
X				sizehints.flags |= USSize;
X			
X#if 1
X			/* XXX This doesn't compile in R3.
X			   It's safe to take it out. */
X			
X			/* Derive the gravity hint from the geometry */
X			
X			if ((flags & XNegative) || (flags & YNegative)) {
X				sizehints.flags |= PWinGravity;
X				if (flags & XNegative) {
X					if (flags & YNegative)
X						sizehints.win_gravity =
X							SouthEastGravity;
X					else
X						sizehints.win_gravity =
X							NorthEastGravity;
X				}
X				else
X					sizehints.win_gravity =
X						SouthWestGravity;
X			}
X#endif			
X			/* XXX Do we need a resource to specify the min
X			   size (or base size), max size and size
X			   increment?  Better to let the application
X			   decide based upon the font used? */
X		}
X	}
X	
X	/* Set the initial geometry from the size hints just computed */
X	win->wo.border= 2*IBORDER;
X	win->wo.x= sizehints.x + win->wo.border;
X	win->wo.y= sizehints.y + win->wo.border;
X	win->wo.width= sizehints.width;
X	win->wo.height= sizehints.height;
X	
X	/* Set the foreground and background pixel values */
X	win->fga= _wgetpixel("foreground", "Foreground",
X						BlackPixelOfScreen(_ws));
X	win->bga= _wgetpixel("background", "Background",
X						WhitePixelOfScreen(_ws));
X	win->fgo= _wgetpixel("menuForeground", "Foreground", win->fga);
X	win->bgo= _wgetpixel("menuBackground", "Background", win->bga);
X	
X	/* Swap the pixel values if 'reverse' specified */
X	if (_wgetbool("reverse", "Reverse", 0)) {
X		unsigned long temp= win->fga;
X		win->fga= win->bga;
X		win->bga= temp;
X		temp= win->fgo;
X		win->fgo= win->bgo;
X		win->bgo= temp;
X	}
X	
X	/* Create the outer Window */
X	if (!_wcreate(&win->wo, RootWindowOfScreen(_ws), 0, FALSE,
X		win->fgo, win->bgo)) {
X		FREE(win);
X		return NULL;
X	}
X	
X	/* Create the inner subWindows */
X	if (!_wmakesubwins(win)) {
X		FREE(win);
X		return NULL;
X	}
X	
X	/* Create the Graphics Contexts */
X	win->gc= _wgcreate(win->wo.wid, _wmf->fid, win->fgo, win->bgo);
X	win->gca= _wgcreate(win->wa.wid, _wf->fid, win->fga, win->bga);
X	
X	/* Change selected Window properties */
X	_wsetmasks(win);
X	_w_setgrayborder(win);
X	
X	/* Set the "invalid" region to empty (rely on Expose events) */
X	win->inval= XCreateRegion();
X	
X	/* Initialize the menu bar stuff */
X	_waddmenus(win);
X	
X	/* Set window properties */
X	
X	/* Set window and icon names */
X	{
X		char *windowname = title;
X		char *iconname = NULL;
X		char *p;
X		
X		/* Resources may override these for the first window */
X		if (!used_defaults) {
X			if ((p = _wgetdefault("title", "Title")) != NULL)
X				windowname = iconname = p;
X			if ((p = _wgetdefault("iconName", "IconName")) != NULL)
X				iconname = p;
X		}
X		
X		XStoreName(_wd, win->wo.wid, windowname);
X		/* Only store the icon name if specified -- the WM will
X		   derive a default from the title otherwise. */
X		if (iconname != NULL)
X			XSetIconName(_wd, win->wo.wid, iconname);
X	}
X	
X	/* Set command line (computed by winitargs()) */
X	XChangeProperty(_wd, win->wo.wid,
X		XA_WM_COMMAND, XA_STRING, 8, PropModeReplace,
X		(unsigned char *)_wm_command, _wm_command_len);
X		/* XXX The ICCCM prescribes that exactly one window
X		   of a given client has this property.  Later. */
X	
X	/* Set normal size hints (computed above) */
X	XSetNormalHints(_wd, win->wo.wid, &sizehints);
X	
X	/* Set WM Hints */
X	{
X		XWMHints wmhints;
X		char *value;
X		wmhints.flags= InputHint | StateHint;
X		wmhints.input= _wgetbool("input", "Input", 1);
X		if (!used_defaults &&
X			_wgetbool("iconic", "Iconic", 0))
X			wmhints.initial_state= IconicState;
X		else
X			wmhints.initial_state= NormalState;
X		if (!used_defaults && (value=
X			_wgetdefault("iconGeometry", "IconGeometry"))
X				!= NULL) {
X			unsigned int width, height;
X			int flags= XParseGeometry(value,
X				&wmhints.icon_x, &wmhints.icon_y,
X				&width, &height);
X			if (flags & XNegative)
X				wmhints.icon_x=
X					WidthOfScreen(_ws) - wmhints.icon_x;
X			if (flags & YNegative)
X				wmhints.icon_y=
X					HeightOfScreen(_ws) - wmhints.icon_y;
X			if (flags & (XValue|YValue)) {
X				wmhints.flags |= IconPositionHint;
X				_wdebug(1, "icon pos: %d,%d",
X					wmhints.icon_x, wmhints.icon_y);
X			}
X			else
X				_wdebug(1, "no icon pos");
X		}
X		value= _wgetdefault("iconBitmap", "IconBitmap");
X		if (value != NULL)  {
X			wmhints.icon_pixmap= readpixmap(value);
X			if (wmhints.icon_pixmap != None)
X				wmhints.flags |= IconPixmapHint;
X		}
X		value= _wgetdefault("iconMask", "IconMask");
X		if (value != NULL)  {
X			wmhints.icon_mask= readbitmap(value);
X			if (wmhints.icon_mask != None)
X				wmhints.flags |= IconMaskHint;
X		}
X		/* XXX What about window groups? */
X		XSetWMHints(_wd, win->wo.wid, &wmhints);
X	}
X	
X	/* Set class (same as strings used by _wgetdefault() */
X	{
X		XClassHint classhint;
X		classhint.res_name= _wprogname;
X		classhint.res_class= "Stdwin";
X		XSetClassHint(_wd, win->wo.wid, &classhint);
X	}
X	
X	/* Set client machine */
X	XChangeProperty(_wd, win->wo.wid,
X		XA_WM_CLIENT_MACHINE, XA_STRING, 8, PropModeReplace,
X		(unsigned char *)_whostname, strlen(_whostname));
X	
X	/* Set protocols property */
X	{
X		static Atom protocols[] = {
X			0 /*XA_WM_DELETE_WINDOW*/,
X		};
X		protocols[0] = _wm_delete_window;
X		XChangeProperty(_wd, win->wo.wid,
X			_wm_protocols, XA_ATOM, 32, PropModeReplace,
X			(unsigned char *)protocols,
X			sizeof(protocols) / sizeof(protocols[0]));
X	}
X	
X	/* Store the WINDOW pointer in the list of windows */
X	L_APPEND(nwins, winlist, WINDOW *, win);
X	
X	/* Now we're ready, finally show the window, on top of others */
X	XMapRaised(_wd, win->wo.wid);
X	
X	/* Note that we've used the once-only defaults */
X	used_defaults= TRUE;
X	
X	/* Don't forget to return the WINDOW pointer */
X	return win;
X}
X
X
X/* Make a window the active window -- ICCCM version.
X   - De-iconify.
X   - Raise the window.
X   Any events that may follow from this are handled later when we get them.
X*/
X
Xvoid
Xwsetactive(win)
X	WINDOW *win;
X{
X	XMapRaised(_wd, win->wo.wid); /* The ICCCM way to de-iconify */
X	XRaiseWindow(_wd, win->wo.wid);
X}
X
X
X/* Get a pixel value from the resource database */
X
Xunsigned long
X_wgetpixel(resname, resclassname, defpixel)
X	char *resname;
X	char *resclassname;
X	unsigned long defpixel;
X{
X	char *cname;
X	Colormap cmap;
X	XColor hard, exact;
X	
X	cname= _wgetdefault(resname, resclassname);
X	if (cname == NULL)
X		return defpixel;
X	cmap= DefaultColormapOfScreen(_ws);
X	if (!XParseColor(_wd, cmap, cname, &exact)) {
X		_wwarning("%s: no such color", cname);
X		return defpixel;
X	}
X	hard= exact;
X	if (!XAllocColor(_wd, cmap, &hard)) {
X		_wwarning("%s: can't allocate color cell", cname);
X		return defpixel;
X	}
X	return hard.pixel;
X}
X
X
X/* Return a gray pixmap */
X
XPixmap
X_w_gray() {
X#include <X11/bitmaps/gray>
X/* defines gray_bits, gray_width, gray_height */
X	
X	static Pixmap gray;
X	
X	if (gray == 0) {
X		gray= XCreatePixmapFromBitmapData(_wd,
X			RootWindowOfScreen(_ws),
X			gray_bits, gray_width, gray_height,
X			BlackPixelOfScreen(_ws),
X			WhitePixelOfScreen(_ws),
X			DefaultDepthOfScreen(_ws));
X	}
X	
X	return gray;
X}
X
X
X/* Set the border pixmap of a window to a gray pattern */
X
Xvoid
X_w_setgrayborder(win)
X	WINDOW *win;
X{
X	XSetWindowBorderPixmap(_wd, win->wo.wid, _w_gray());
X}
X
X
X/* Create a Graphics Context using the given Window and Font ids.
X   Don't set the font if Font id is zero.
X   The plane mask is set to the XOR of the fg and bg colors;
X   if the window bg color is the same as the GC bg color,
X   this makes painting, erasing and XOR'ing possible
X   on color displays.  (On monochrome displays, one of the colors
X   is 1, one is 0, so the XOR is 1, which means all the planes we have.
X*/
X
XGC
X_wgcreate(wid, fid, fg, bg)
X	Window wid;
X	Font fid;
X	unsigned long fg, bg;
X{
X	int mask= GCForeground|GCBackground|GCPlaneMask;
X	XGCValues v;
X	
X	v.plane_mask= fg ^ bg;
X	v.foreground= fg;
X	v.background= bg;
X	if (fid != 0) {
X		v.font= fid;
X		mask |= GCFont;
X	}
X	return XCreateGC(_wd, wid, mask, &v);
X}
X
X
X/* Create a Window for a given windata struct and set its event mask.
X   If map is TRUE, also map it.
X   Initially, a window is not dirty (it'll get Expose events for that) */
X
Xbool
X_wcreate1(wp, parent, cursor, map, fg, bg, nowm)
X	struct windata *wp;
X	Window parent;
X	int cursor;
X	bool map;
X	unsigned long fg, bg;
X	bool nowm;
X{
X	XSetWindowAttributes attributes;
X	unsigned long valuemask=
X		CWBackPixel|CWBorderPixel|CWBitGravity|CWBackingStore;
X	
X	attributes.background_pixel= bg;
X	attributes.border_pixel= fg;
X	attributes.bit_gravity= NorthWestGravity;
X	if (nowm) {
X		attributes.override_redirect = 1;
X		valuemask |= CWOverrideRedirect;
X	}
X/*
X	attributes.backing_store= WhenMapped;
X*/
X	attributes.backing_store= NotUseful; /* Seems to be Harmful... */
X
X	if (cursor > 0) {
X		attributes.cursor= XCreateFontCursor(_wd, cursor);
X		valuemask |= CWCursor;
X	}
X	
X	/* We must subtract border width from x and y before
X	   passing them to WCreateSimpleWindow, since
X	   they refer to the upper left corner of the border! */
X	
X	wp->wid= XCreateWindow(
X			_wd,
X			parent,
X			wp->x - wp->border,	/* x */
X			wp->y - wp->border,	/* y */
X			wp->width,
X			wp->height,
X			wp->border,		/* border width */
X			CopyFromParent,		/* depth */
X			InputOutput,		/* class */
X			CopyFromParent,		/* visual */
X			valuemask,
X			&attributes
X		);
X	if (!wp->wid) {
X		_werror("_wcreate: can't create (sub)Window");
X		return FALSE;
X	}
X	_wdebug(2, "_wcreate: wid=0x%x", wp->wid);
X	if (map)
X		XMapWindow(_wd, wp->wid);
X	wp->dirty= FALSE;
X	return TRUE;
X}
X
X
X/* Set the save-under property for a window given by a windata struct */
X
Xvoid
X_wsaveunder(wp, flag)
X	struct windata *wp;
X	Bool flag;
X{
X	static int saveUnder = -1;
X	XSetWindowAttributes attrs;
X	
X	/* The user may explicitly turn off save-unders by specifying
X		Stdwin*SaveUnder: off
X	   since they are broken on some servers */
X	
X	if (saveUnder < 0) {
X		/* First time here: check resource database */
X		saveUnder = _wgetbool("saveUnder", "SaveUnder", 1);
X		if (!saveUnder)
X			_wdebug(1, "user doesn't want save-unders");
X	}
X	
X	if (!saveUnder)
X		return;
X	
X	attrs.save_under= flag;
X	XChangeWindowAttributes(_wd, wp->wid, CWSaveUnder, &attrs);
X}
X
X
X/* Set gravity for a window given by a windata struct */
X
Xstatic void
X_wgravity(wp, grav)
X	struct windata *wp;
X	int grav;
X{
X	XSetWindowAttributes attrs;
X	attrs.win_gravity= grav;
X	XChangeWindowAttributes(_wd, wp->wid, CWWinGravity, &attrs);
X}
X
X
X/* Move and resize a window given by a windata struct */
X
Xvoid
X_wmove(wp)
X	struct windata *wp;
X{
X	XMoveResizeWindow(_wd, wp->wid,
X	    wp->x - wp->border, wp->y - wp->border, wp->width, wp->height);
X}
X
X
X/* (Re)compute the sizes and positions of all subwindows.
X   If aflag is set, the application subwindow is resized as well.
X   Note a check (in SZ) to prevent windows ever to get a size <= 0 */
X
Xstatic
X_wsizesubwins(win)
X	WINDOW *win;
X{
X	int bmargin= win->wi.height - win->doc.height - win->wa.y;
X	int rmargin= win->wi.width - win->doc.width - win->wa.x;
X
X#define SZ(elem, nx, ny, nw, nh, nb) \
X		(win->elem.x= (nx), \
X		win->elem.y= (ny), \
X		win->elem.width= (nw) > 0 ? (nw) : 1, \
X		win->elem.height= (nh) > 0 ? (nh) : 1, \
X		win->elem.border= (nb))
X
X	/* Interior window in the middle */
X	SZ(wi, LMARGIN, TMARGIN,
X		win->wo.width - LMARGIN - RMARGIN,
X		win->wo.height - TMARGIN - BMARGIN, 0);
X	/* Menu bar at the top */
X	SZ(mbar, 0, 0, win->wo.width, TMARGIN - IBORDER, IBORDER);
X	/* Vbar left */
X	SZ(vbar, 0, win->wi.y, LMARGIN - IBORDER, win->wi.height, IBORDER);
X	/* Hbar at bottom (top would be ugly because of mbar) */
X	SZ(hbar, win->wi.x, win->wo.height - BMARGIN + IBORDER,
X		win->wi.width, BMARGIN - IBORDER, IBORDER);
X
X#undef SZ
X	
X	/* The document window (wa) is sized differently.
X	   If it fits in the window, it is made the same size
X	   and aligned at (0,0) no questions asked.
X	   Otherwise, it is moved so that the window never shows more
X	   outside it than before (if at all possible). */
X	
X	if (win->doc.width <= win->wi.width) {
X		win->wa.x= IMARGIN;
X		win->wa.width= win->wi.width;
X	}
X	else {
X		CLIPMIN(rmargin, IMARGIN);
X		CLIPMIN(win->wa.x, win->wi.width - win->wa.width - rmargin);
X		CLIPMAX(win->wa.x, IMARGIN);
X		win->wa.width= win->doc.width;
X		CLIPMIN(win->wa.width, win->wi.width - win->wa.x);
X	}
X	if (win->doc.height <= win->wi.height) {
X		win->wa.y= 0;
X		win->wa.height= win->wi.height;
X	}
X	else {
X		CLIPMIN(bmargin, 0);
X		CLIPMIN(win->wa.y, win->wi.height - win->wa.height - bmargin);
X		CLIPMAX(win->wa.y, 0);
X		win->wa.height= win->doc.height;
X		CLIPMIN(win->wa.height, win->wi.height - win->wa.y);
X	}
X}
X
X
X/* Create the permanently visible subwindows.
X   Return TRUE if all creations succeeded.
X   The inner window is created after all bars, so it lies on top,
X   and will receive clicks in its border */
X
Xstatic bool
X_wmakesubwins(win)
X	WINDOW *win;
X{
X	Window w= win->wo.wid;
X	unsigned long fg= win->fgo, bg= win->bgo;
X	
X	_wsizesubwins(win);
X	if (!(	_wcreate(&win->mbar, w, XC_arrow, TRUE, fg, bg) &&
X		_wcreate(&win->vbar, w, XC_sb_v_double_arrow, TRUE, fg, bg) &&
X		_wcreate(&win->hbar, w, XC_sb_h_double_arrow, TRUE, fg, bg) &&
X		_wcreate(&win->wi, w, 0, TRUE, fg, bg) &&
X		_wcreate(&win->wa, win->wi.wid, XC_crosshair, TRUE,
X			fg, win->bga)))
X		return FALSE;
X	_wgravity(&win->hbar, SouthWestGravity);
X	return TRUE;
X}
X
X
X/* Resize all subwindows and move them to their new positions.
X   The application and current menu windows (WA, MWIN) are not resized.
X   The application window may be moved, however, to prevent exposing
X   parts outside the document that weren't visible earlier. */
X
Xvoid
X_wmovesubwins(win)
X	WINDOW *win;
X{
X	int i;
X	_wsizesubwins(win);
X	
X	for (i= 1; i <= WA; ++i)
X		_wmove(&win->subw[i]);
X	
X	/* Invalidate scroll bars after a resize */
X	win->hbar.dirty= win->vbar.dirty= _w_dirty= TRUE;
X}
X
X
X/* Set normal event masks for all (sub)Windows of a WINDOW */
X
Xvoid
X_wsetmasks(win)
X	WINDOW *win;
X{
X	int i;
X	
X	XSelectInput(_wd, win->wo.wid, OUTER_MASK);
X	for (i= 1; i <= WA; ++i) {
X		if (win->subw[i].wid != 0)
X			XSelectInput(_wd, win->subw[i].wid,
X				(i == WI) ? NoEventMask : OTHER_MASK);
X	}
X}
X
X
X/* Generate any pending size event. */
X
Xbool
X_w_doresizes(ep)
X	EVENT *ep;
X{
X	int i;
X
X	for (i= nwins; --i >= 0; ) {
X		WINDOW *win= winlist[i];
X		if (win->resized) {
X			win->resized= FALSE;
X			ep->type= WE_SIZE;
X			ep->window= win;
X			if (i == 0)
X				_w_resized= FALSE;
X			return TRUE;
X		}
X	}
X	_w_resized= FALSE;
X	return FALSE;
X}
X
X
X/* Perform any pending window updates.
X   If the application subwindow needs an update,
X   either call its draw procedure or generate a WE_DRAW event
X   in the given event record (and then stop).
X   Return TRUE if an event was generated */
X
Xbool
X_w_doupdates(ep)
X	EVENT *ep;
X{
X	int i;
X
X	for (i= nwins; --i >= 0; ) {
X		if (update(winlist[i], ep)) {
X			if (i == 0)
X				_w_dirty= FALSE;
X			return TRUE;
X		}
X	}
X	_w_dirty= FALSE;
X	return FALSE;
X}
X
Xvoid
Xwupdate(win)
X	WINDOW *win;
X{
X	(void) update(win, (EVENT *)NULL);
X}
X
X/* Update any parts of a window that need updating.
X   If the application window needs redrawing and there is no drawproc
X   and the 'ep' argument is non-nil,
X   construct a DRAW event and return TRUE. */
X
Xstatic bool
Xupdate(win, ep)
X	WINDOW *win;
X	EVENT *ep;
X{
X	bool ret= FALSE;
X	
X	if (win->mbar.dirty)
X		_wdrawmbar(win);
X	if (win->hbar.dirty)
X		_wdrawhbar(win);
X	if (win->vbar.dirty)
X		_wdrawvbar(win);
X	win->mbar.dirty= win->hbar.dirty= win->vbar.dirty= FALSE;
X	/* wi and wo have nothing that can be drawn! */
X	
X	if (win->wa.dirty && (win->drawproc != NULL || ep != NULL)) {
X		XRectangle clip;
X		int left, top, right, bottom;
X		XClipBox(win->inval, &clip);
X		left= clip.x;
X		top= clip.y;
X		right= left + clip.width;
X		bottom= top + clip.height;
X		CLIPMIN(left, -win->wa.x);
X		CLIPMIN(top, -win->wa.y);
X		CLIPMAX(right, win->wi.width - win->wa.x);
X		CLIPMAX(bottom, win->wi.height - win->wa.y);
X		_wdebug(3, "clip: (%d,%d) to (%d,%d)",
X			left, top, right, bottom);
X		if (left < right && top < bottom) {
X			_whidecaret(win);
X			if (win->drawproc != NULL) {
X				/* A bug in X11R2 XSetRegion prevents this
X				   from working: */
X#ifndef R2
X				/* Version for R3 or later */
X				XSetRegion(_wd, win->gca, win->inval);
X#else
X				/* Version for R2 */
X				XSetClipRectangles(_wd, win->gca,
X					0, 0, &clip, 1, Unsorted);
X#endif
X				wbegindrawing(win);
X				werase(left, top, right, bottom);
X				(*win->drawproc)(win,
X					left, top, right, bottom);
X				wenddrawing(win);
X				XSetClipMask(_wd, win->gca, (Pixmap)None);
X			}
X			else {
X				XClearArea(_wd, win->wa.wid,
X					clip.x, clip.y,
X					clip.width, clip.height, FALSE);
X				ep->type= WE_DRAW;
X				ep->window= win;
X				ep->u.area.left= left;
X				ep->u.area.top= top;
X				ep->u.area.right= right;
X				ep->u.area.bottom= bottom;
X				ret= TRUE;
X			}
X			_wshowcaret(win);
X		}
X		XDestroyRegion(win->inval);
X		win->inval= XCreateRegion();
X		win->wa.dirty= FALSE;
X	}
X	return ret;
X}
X
X
X/* Close a window */
X
Xvoid
Xwclose(win)
X	WINDOW *win;
X{
X	int i;
X	for (i= 0; i < nwins; ++i) {
X		if (winlist[i] == win)
X			break;
X	}
X	if (i >= nwins) {
X		_werror("wclose: unknown window");
X		return;
X	}
X	L_REMOVE(nwins, winlist, WINDOW *, i);
X	_w_deactivate(win);
X	_w_delmenus(win);
X	XFreeGC(_wd, win->gc);
X	XFreeGC(_wd, win->gca);
X	XDestroyWindow(_wd, win->wo.wid);
X	XFlush(_wd); /* Make the effect immediate */
X	FREE(win);
X}
X
X
X/* Change a window's title */
X
Xvoid
Xwsettitle(win, title)
X	WINDOW *win;
X	char *title;
X{
X	XStoreName(_wd, win->wo.wid, title);
X	/* The icon name will not change */
X}
X
X
X/* Return a window's current title -- straight from the window property */
X
Xchar *
Xwgettitle(win)
X	WINDOW *win;
X{
X	static char *title = NULL;
X	
X	if (title != NULL)
X		free(title); /* Free result of previous call */
X	title = NULL; /* Just in case */
X	if (!XFetchName(_wd, win->wo.wid, &title))
X		_wwarning("wgettitle: no title set");
X	return title;
X}
X
X
X/* Change a window's icon name */
X
Xvoid
Xwseticontitle(win, title)
X	WINDOW *win;
X	char *title;
X{
X	XSetIconName(_wd, win->wo.wid, title);
X}
X
X
X/* Return a window's current icon name -- straight from the window property */
X
Xchar *
Xwgeticontitle(win)
X	WINDOW *win;
X{
X	static char *title = NULL;
X	
X	if (title != NULL)
X		free(title); /* Free result of previous call */
X	title = NULL; /* Just in case */
X	if (!XGetIconName(_wd, win->wo.wid, &title))
X		_wdebug(1, "wgeticontitle: no icon name set");
X		/* This may occur normally so don't make it a warning */
X	return title;
X}
X
X
X/* Get a window's size.
X   This is really the size of the visible part of the document. */
X
Xvoid
Xwgetwinsize(win, pwidth, pheight)
X	WINDOW *win;
X	int *pwidth, *pheight;
X{
X	*pwidth= win->wi.width - IMARGIN;
X	*pheight= win->wi.height;
X}
X
X
X/* Get a window's position relative to the root */
X
Xvoid
Xwgetwinpos(win, ph, pv)
X	WINDOW *win;
X	int *ph, *pv;
X{
X	Window child;
X	
X	if (!XTranslateCoordinates(	_wd,
X					win->wo.wid,
X					RootWindowOfScreen(_ws),
X					0, 0,
X					ph, pv,
X					&child)) {
X		
X		_wwarning("wgetwinpos: XTranslateCoordinates failed");
X		*ph = 0;
X		*pv = 0;
X	}
X}
X
X
X/* "Change" part of a document, i.e., post a WE_DRAW event */
X
Xvoid
Xwchange(win, left, top, right, bottom)
X	WINDOW *win;
X	int left, top, right, bottom;
X{
X	_wdebug(3, "wchange: %d %d %d %d", left, top, right, bottom);
X	if (left < right && top < bottom) {
X		XRectangle r;
X		r.x= left;
X		r.y= top;
X		r.width= right - left;
X		r.height= bottom - top;
X		XUnionRectWithRegion(&r, win->inval, win->inval);
X		win->wa.dirty= TRUE;
X		_w_dirty= TRUE;
X	}
X}
X
X
X/* "Scroll" part of a document, i.e., copy bits and erase the place
X   where they came from.  May cause WE_DRAW events if source bits are
X   covered. */
X
Xvoid
Xwscroll(win, left, top, right, bottom, dh, dv)
X	WINDOW *win;
X	int left, top, right, bottom;
X	int dh, dv;
X{
X	int src_x= left, src_y= top;
X	int dest_x= left, dest_y= top;
X	int width= right - left - ABS(dh);
X	int height= bottom - top - ABS(dv);
X	
X	if (dh == 0 && dv == 0)
X		return;
X	
X	if (dh < 0)
X		src_x += -dh;
X	else
X		dest_x += dh;
X	if (dv < 0)
X		src_y += -dv;
X	else
X		dest_y += dv;
X	
X	_wdebug(2, "wscroll: src(%d,%d)size(%d,%d)dest(%d,%d)",
X		src_x, src_y, width, height, dest_x, dest_y);
X	_whidecaret(win);
X	XCopyArea(_wd, win->wa.wid, win->wa.wid, win->gca,
X		src_x, src_y, width, height, dest_x, dest_y);
X	_wshowcaret(win);
X	
X	if (XRectInRegion(win->inval, left, top, right-left, bottom-top)
X							!= RectangleOut) {
X		/* Scroll the overlap between win->inval and the
X		   scrolled rectangle:
X			scroll := <the entire scrolling rectangle>
X			overlap := inval * scroll
X			inval -:= overlap
X			shift overlap by (dh, dv)
X			overlap := overlap * scroll
X			inval +:= overlap
X		*/
X		Region scroll = XCreateRegion();
X		Region overlap = XCreateRegion();
X		XRectangle r;
X		r.x = left;
X		r.y = top;
X		r.width = right-left;
X		r.height = bottom-top;
X		XUnionRectWithRegion(&r, scroll, scroll);
X		XIntersectRegion(win->inval, scroll, overlap);
X		XSubtractRegion(win->inval, overlap, win->inval);
X		XOffsetRegion(overlap, dh, dv);
X		XIntersectRegion(overlap, scroll, overlap);
X		XUnionRegion(win->inval, overlap, win->inval);
X		XDestroyRegion(overlap);
X		XDestroyRegion(scroll);
X
X		/* There's still a bug left: exposure events in the queue
X		   (like GraphicsExposures caused be previous scrolls!)
X		   should be offset as well, bu this is too complicated
X		   to fix right now... */
X	}
X	
X	/* Clear the bits scrolled in */
X	
X	if (dh > 0)
X		XClearArea(_wd, win->wa.wid,
X			left, top, dh, bottom-top, FALSE);
X	else if (dh < 0)
X		XClearArea(_wd, win->wa.wid,
X			right+dh, top, -dh, bottom-top, FALSE);
X	if (dv > 0)
X		XClearArea(_wd, win->wa.wid,
X			left, top, right-left, dv, FALSE);
X	else if (dv < 0)
X		XClearArea(_wd, win->wa.wid,
X			left, bottom+dv, right-left, -dv, FALSE);
X}
X
X
X/* Change the "origin" of a window (position of document, really) */
X
Xvoid
Xwsetorigin(win, orgh, orgv)
X	WINDOW *win;
X	int orgh, orgv;
X{
X	bool moveit= FALSE;
X	
X	CLIPMIN(orgh, 0);
X	CLIPMIN(orgv, 0);
X	if (win->wa.x != -orgh) {
X		win->hbar.dirty= moveit= TRUE;
X		win->wa.x= -orgh;
X	}
X	if (win->wa.y != -orgv) {
X		win->vbar.dirty= moveit= TRUE;
X		win->wa.y= -orgv;
X	}
X	if (moveit)
X		XMoveWindow(_wd, win->wa.wid, -orgh, -orgv);
X}
X
X
X/* Get the "origin" (see above) of a window */
X
Xvoid
Xwgetorigin(win, ph, pv)
X	WINDOW *win;
X	int *ph, *pv;
X{
X	*ph= -win->wa.x;
X	CLIPMIN(*ph, 0);
X	*pv= -win->wa.y;
X}
X
X
X/* Set the document size.  Zero means don't use a scroll bar */
X
Xvoid
Xwsetdocsize(win, width, height)
X	WINDOW *win;
X	int width, height;
X{
X	bool dirty= FALSE;
X	
X	CLIPMIN(width, 0);
X	CLIPMIN(height, 0);
X	if (win->doc.width != width) {
X		win->doc.width= width;
X		if (width <= win->wo.width - IMARGIN) {
X			win->wa.x= IMARGIN;
X			win->wa.width= win->wi.width;
X		}
X		else {
X			win->wa.width= width;
X			CLIPMIN(win->wa.width, win->wi.width - win->wa.x);
X		}
X		win->hbar.dirty= dirty= TRUE;
X	}
X	if (win->doc.height != height) {
X		win->doc.height= height;
X		if (height <= win->wo.height) {
X			win->wa.y= 0;
X			win->wa.height= win->wi.height;
X		}
X		else {
X			win->wa.height= height;
X		}
X		win->vbar.dirty= dirty= TRUE;
X	}
X	if (dirty) {
X		_w_dirty= TRUE;
X		_wmove(&win->wa);
X	}
X}
X
X
X/* Return the document size last set by wsetdocsize() */
X
Xvoid
Xwgetdocsize(win, pwidth, pheight)
X	WINDOW *win;
X	int *pwidth, *pheight;
X{
X	*pwidth = win->doc.width;
X	*pheight = win->doc.height;
X}
X
X
X/* Change the cursor for a window */
X
Xvoid
Xwsetwincursor(win, cursor)
X	WINDOW *win;
X	CURSOR *cursor;
X{
X	Cursor c;
X	if (cursor == NULL)
X		c = None;
X	else
X		c = (Cursor) cursor;
X	XDefineCursor(_wd, win->wa.wid, c);
X}
X
X
X/* Scroll the document in the window to ensure that the given rectangle
X   is visible, if at all possible.  Don't scroll more than necessary. */
X
Xvoid
Xwshow(win, left, top, right, bottom)
X	WINDOW *win;
X	int left, top, right, bottom;
X{
X	int orgh= -win->wa.x;
X	int orgv= -win->wa.y;
X	int winwidth, winheight;
X	int extrah, extrav;
X	
X	_wdebug(3, "wshow: %d %d %d %d", left, top, right, bottom);
X	
X	wgetwinsize(win, &winwidth, &winheight);
X	
X	if (left >= orgh &&
X		top >= orgv &&
X		right <= orgh + winwidth &&
X		bottom <= orgv + winheight)
X		return; /* Already visible */
X	
X	extrah= (winwidth - (right - left)) / 2;
X	CLIPMAX(extrah, win->doc.width - right);
X	CLIPMIN(extrah, 0);
X	orgh= right + extrah - winwidth;
X	CLIPMAX(orgh, left);
X	CLIPMIN(orgv, 0);
X	
X	extrav= (winheight - (bottom - top)) / 2;
X	CLIPMAX(extrav, win->doc.height - bottom);
X	CLIPMIN(extrav, 0);
X	orgv= bottom + extrav - winheight;
X	CLIPMAX(orgv, top);
X	CLIPMIN(orgv, 0);
X	
X	wsetorigin(win, orgh, orgv);
X}
X
X
X/* Sound the bell (beep) */
X
Xvoid
Xwfleep()
X{
X	XBell(_wd, 0);
X}
X
X
X/* Helper functions for the menu mananger: */
X
X_waddtoall(mp)
X	MENU *mp;
X{
X	int i;
X	
X	for (i= nwins; --i >= 0; )
X		wmenuattach(winlist[i], mp);
X}
X
X
X_wdelfromall(mp)
X	MENU *mp;
X{
X	int i;
X	
X	for (i= nwins; --i >= 0; )
X		wmenudetach(winlist[i], mp);
X}
X
X
X/* Helper function for the timer manager: */
X
XWINDOW *
X_wnexttimer()
X{
X	int i;
X	WINDOW *cand= NULL;
X	
X	for (i= nwins; --i >= 0; ) {
X		WINDOW *win= winlist[i];
X		long t= win->timer;
X		if (t != 0) {
X			if (cand == NULL || t < cand->timer)
X				cand= win;
X		}
X	}
X	return cand;
X}
X
X
X/* Delete all windows -- called by wdone() */
X
X_wkillwindows()
X{
X	while (nwins > 0)
X		wclose(winlist[nwins-1]);
X}
END_OF_FILE
if test 30704 -ne `wc -c <'Ports/x11/window.c'`; then
    echo shar: \"'Ports/x11/window.c'\" unpacked with wrong size!
fi
# end of 'Ports/x11/window.c'
fi
echo shar: End of archive 4 \(of 19\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 19 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0