/* SWiM version 3.1       	v1.0 written  July-August, 1990
 *				v2.0 finished November 22, 1991
 *				v3.0 finished September 17, 1992
 *				v3.1 finished September 22, 1992
 *
 *  SWiM - "Steve's Window Manager"
 *
 *  by Steve Jennen, student (freshman!) programmer
 *
 *  Academic Computer Services
 *  St. Cloud State University, St. Cloud, MN
 *
 *	jennen@tigger.stcloud.msus.edu
 *	jennen@msus1.msus.edu
 *
 *  SWiM Copyright 1990,1991 St. Cloud State University, St. Cloud, MN
 *
 *
 *
 *  I'd like to thank two people who helped me during the development of
 *  this program:
 *
 *  Gordie Schmitt, Academic Computer Services, St. Cloud State University
 *   who helped me with some VAX/VMS system calls and who called DEC when I
 *   found a bug in VAX C/SMG$ - I knew it wasn't MY program
 *
 *  Randy Poser, student, St. John's University, Collegeville, MN
 *   who gave me ideas for the program, and who uses SWiM - so he found
 *   quite a few bugs too
 *
 *
 *  SWiM was brought to you by the letter R.
 *
 *
 * File:  SWiM.C
 *
 *
 *  This is the main program.
 *
 *
 * Modification history:
 *
 *	7-1-91		Steve Jennen
 *
 *			Added default process name creation.  Processes created will be called USERNAME_Sn, where n
 *			is the window number.  This was suggested by Dave Horvath (HORVATH_DB@SCOV19.dnet.ge.com).
 *			Thanx Dave.
 *
 *			Added task list feature.  You may now set up a text file (which must be called task_list.swim)
 *			with tasks listed and default window locations and sizes like:
 *
 *			Name	X	Y	Height	Width	Switch	<------	task_list.swim must not contain these
 *			----------------------------------------------	<------ first two lines.
 *			BBS	0	26	24	80	B
 *			Eeyore	0	1	24	132	E
 *			Mail	0	1	24	132	M
 *			Phone	0	10	24	80	P
 *			Swing	0	1	24	132	S
 *
 *			When you type the hot-key and press T, you are presented with the task list.  Select a task and
 *			a process will be created in a new window with a proces name of USERNAMExNN, where x is the
 *			Switch character from the task_list.swim file and NN should be stripped off.  To actually 'run'
 *			the application, you must put code in your login.com to start the application like:
 *
 *			$ procname = f$getjpi("","PRCNAM")
 *			$ nprocnam = F$EXTRACT(0,f$length(procname)-2,procname)
 *			$ if nprocnam .eqs. "JENNENS" then goto doswing
 *			$ if nprocnam .eqs. "JENNENM" then goto domail
 *			$ exit
 *			$domail:
 * 			$ set term/width=132
 *			$ define/nolog sys$input tt:
 *			$ define/nolog sys$output tt:
 *			$ mail
 *			$ logoutnow
 *			$doswing:
 *			$ set term/width=132
 *			$ define/nolog sys$input tt:
 *			$ define/nolog sys$output tt:
 *			$ swing
 *			$ logoutnow
 *			$done:
 *
 *	7-17-91		Steve Jennen
 *
 *			Fixed the cut/paste bug.  The data from the cut buffer was being output to the PT too fast
 *			and caused a data over run to the terminal causing some characters to be lost, and turning
 *			on the xoff/xon checking causing SWiM to wait forever.
 *
 *			Thanx to Tim Ells for pointing this out, ells%scsd.dnet@gte.com
 *
 *	7-17-91		Steve Jennen
 *
 *			Fixed data logging in stripped mode.  Seems that RMS, like SMG, is not reentrant...gotta turn
 *			them ASTs off.  As a bonus, I also added the recognition of the TAB char in stripped mode.
 *
 *
 *	8-1-91		Steve Jennen
 *
 *			Added the ability to cut data from the backup buffer.  Now, when the cursor hits the top of the
 *			screen during the 'cut' comand, the screen will scroll down.
 *
 *	10-29-91	Steve Jennen
 *
 *			The PREV SCRN and NEXT SCRN keys may now be used in backup or cut modes.  In cut mode, the CTRL-E
 *			key will go to the end of a line, and a CTRL-H will go to the beginning of a line.
 *
 *	11-6-91		Steve Jennen
 *
 *			Processes are now given a process name in the form of:
 *
 *			USERNAMExNN, where x is either a switch character or the number of the window.  NN is the last
 *			two hex digits of the parent process that is actually running the SWiM image.
 *
 *
 *	9-17-92		Anthony C. McCracken
 *
 *			Added support for VMS 5.4 and up FTA terminals.  This
 *			program can now use either the public domain PY/TW
 *			driver or the VMS FT pseudo terminals.  To use FTAs
 *			compile the program with CC/DEFINE=(FTA=1)
 *
 *			Fixed a bug where process names with imbedded non-
 *			printable characters were being generated.
 *
 *
 *	9-22-92		Anthony C. McCracken
 *
 *			Added patches as per Steve Jennen's request to 
 *
 *			1) handle 8-bit characters (international characters)
 *			2) changed the type of key to short int for SMG$READ_KEYSTROKE
 *			3) changed SMG$K_TRM_CTRLZ to SMG$K_TRM_US, to make life nicer for Emacs users
 *			4) added a new conditional compiling directive. Defining SAFE = 1 causes the
 *			   original error checking to take place.  Leaving it undefined speeds up
 *			   SWIM at the expense of losing the LIB$SIGNAL calls.  Define SAFE for debugging.
 *
 *			   By re-arranging the SYS$SETAST calls to enclose only critical sections
 *			   of code and putting calls to SYS$CLRAST in the AST routines I have made
 *			   the ASTs interuptable.  This allows multiple windows to be updated 
 *			   simultaneously while keyboard I/O is taking place.  True FULL-DUPLEX.
 *
 */


#include <iodef.h>
#include <stdio.h>
#include <ttdef.h>
#include <tt2def.h>
#include <starlet.h>
#include <prcdef.h>
#include <trmdef.h>             /* include files for VAX SMG$ interface */
#include <smgdef.h>
#include <smg$routines.h>
#include <descrip.h>
#include <ssdef.h>
#include <dvidef.h>
#include <jpidef.h>
#include <lnmdef.h>
#include <psldef.h>


#include "types.h"		/* SWiM's types	*/

#if FTA
#ifndef vaxpage
#define PAGESIZE 65536 /* max Alpha pagesize */
#define PGPP 128 /* pagelets per page */
#else
#define PAGESIZE 512   /* Vax page size */
#define PGPP 1
#endif
#include "ptddef.h"		/* PTD$routine stuff, FTA driver */
#endif


$DESCRIPTOR( kbd_devnam, "tt:" );	/* user's terminal	*/

#if FTA
   /* do nothing yet */
#else
$DESCRIPTOR( wdevnam, "PYA0:" );	/* PYA0: pseudo terminal control device	*/
#endif



/* called whenever an out of band broadcast is received at the terminal */
/* all control chars such as t, y, c are disabled while the menu is on	*/

void get_out_of_band_asts()
{
    /* null function */
}




/* This should never be called.	*/

void get_broadcast()
{
    /* null function */
}





/* Function to get the controlling process's IO byte count quota.
 *
 * Input  : nothing
 * Output : the_quota
 *
 * During development, I ran into a problem where a process got stuck in a MUTEX state
 * because it ran out of io_byte_count_quota.  SWiM checks the necessary quota needed to
 * create a session and will not allow the user to create a session if there is insufficent
 * quota.
 */


int get_quota()
{
 struct itmlst item_list;
 int	quota,
	quota_len;

	quota_len = 4;
	item_list.buflen = 4;
	item_list.item   = JPI$_BYTCNT;
	item_list.bufadr = &quota;
	item_list.lenadr = &quota_len;
	item_list.zero1  = 0;
	item_list.zero2  = 0;

	status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	return(quota);
}





unsigned int get_pid()
{
 struct itmlst item_list;
 unsigned int pid;
 int    pid_len;

	pid_len = 4;
	item_list.buflen = 4;
	item_list.item   = JPI$_PID;
	item_list.bufadr = &pid;
	item_list.lenadr = &pid_len;
	item_list.zero1  = 0;
	item_list.zero2  = 0;

	status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	return(pid);
}





void get_username(char *username)
{
 struct itmlst item_list;
 int	i, quota_len;

	quota_len = 4;
	item_list.buflen = 12;
	item_list.item   = JPI$_USERNAME;
	item_list.bufadr = username;
	item_list.lenadr = &quota_len;
	item_list.zero1  = 0;
	item_list.zero2  = 0;

	status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	i = 0;
	while ((i < 12) && (username[i] != ' ')) i++;
	username[i]='\0';
}





/* Function to get the controlling process's remaining AST quota
 *
 * Input  : nothing
 * Output : the_quota
 *
 */


int get_ast_quota()
{
 struct itmlst item_list;
 int	quota,
	quota_len;

	quota_len = 4;
	item_list.buflen = 4;
	item_list.item   = JPI$_ASTCNT;
	item_list.bufadr = &quota;
	item_list.lenadr = &quota_len;
	item_list.zero1  = 0;
	item_list.zero2  = 0;

	status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	return(quota);
}





/* Get the controlling process's base priority.  This is used in the $CREPRC call.
 *
 * Input  : none
 * Output : the_priority
 */


int get_priority()
{
 struct itmlst item_list;
 int	priority,
	priority_len;

	priority_len = 4;
	item_list.buflen = 4;
	item_list.item   = JPI$_PRIB;
	item_list.bufadr = &priority;
	item_list.lenadr = &priority_len;
	item_list.zero1  = 0;
	item_list.zero2  = 0;

	status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	return(priority);
}





#if DEBUG			/* used only during development	*/

void up_mony()
{
	if (++mony>data_rows)
	{
		mony=data_rows;
	}
}

#endif





/* move the virtual display cursor.  only the cursor in the active display
 * is updated constantly mainly for use in the editor
 */


void cursor( int x, int y )
{
 int real_y;

		real_y = y + BACKSCROLLLINES;
		sys$setast(0);
		smg$set_cursor_abs( &current_display, &real_y, &x );
		sys$setast(1);
}





/* put a string to the current virtual display using the current rendition
 * type.
 */


void puts_smg( char *string, int x, int y, int graphic )
{
 vax$u_longword flags = 0,
		char_set;

	if ( graphic )					/* if terminal is printing graphics characters	*/
         char_set = SMG$C_SPEC_GRAPHICS;
	else char_set = SMG$C_ASCII;

 	puts_string.dsc$w_length = strlen( string );	/* length of name */
 	puts_string.dsc$a_pointer = string;		/* address of string */

	smg$put_chars( &current_display, &puts_string, &y, &x, 0, &rendition, 0, &char_set );
}





/* put a string to the current virtual display (window) using the current rendition
 * type.  this procedure adds the offset constant to the y coordinate to place the
 * text in the visible portion of the virtual display.
 */


void puts_smg_w( char *string, int x, int y, int graphic, int insert, int wide, int highwide )
{
 vax$u_longword char_set;
 int real_y;

	if ( graphic )					/* if terminal is printing graphics characters	*/
         char_set = SMG$C_SPEC_GRAPHICS;
	else char_set = SMG$C_ASCII;

 	puts_string.dsc$w_length = strlen( string );	/* length of name */
 	puts_string.dsc$a_pointer = string;		/* address of string */

	real_y = y + BACKSCROLLLINES;
	if ( insert )
	 smg$insert_chars( &current_display, &puts_string, &real_y, &x, &rendition, 0, &char_set );
	else
	{
		if ( wide )
		{
			x *= 2;
			smg$put_chars_wide( &current_display, &puts_string, &real_y, &x, &rendition, 0, &char_set );
		}
		else if ( highwide )
		{
			x *= 2;
			smg$put_chars_highwide( &current_display, &puts_string, &real_y, &x, &rendition, 0, &char_set );
		}
		else
		 smg$put_chars( &current_display, &puts_string, &real_y, &x, 0, &rendition, 0, &char_set );
	}
}





/* puts with NewLine by calling puts_smg with string and then checking
 * scroll, if necessary
 */


void puts_nl_smg( char *string, int x, int y, int graphic )
{
 vax$u_longword disp_height;	/* obvious */

						/* get virtual display height */

	smg$get_display_attr( &current_display, &disp_height );

 	puts_smg( string, x, y, graphic );

 	if (y == disp_height) smg$scroll_display_area( &current_display );
}






#if DEBUG

void data_on()
{
	sys$setast(0);
	status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}

#endif




/* Setup error virtual display.	*/


void setup_error()
{
	sys$setast(0);
	status = smg$erase_display( &error_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = smg$paste_virtual_display( &error_id, &pasteboard_id, &error_y, &error_x );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Remove error virtual display.	*/


void remove_error()
{
	sys$setast(0);
	status = smg$unpaste_virtual_display( &error_id, &pasteboard_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}




/* quickly change the term characteristics so we can set up an smg$ virtual keyboard and
 * have it act normal.
 */


void setup_keyboard()
{
 unsigned int noecho = TT$M_NOECHO, past = TT2$M_PASTHRU;

	sys$setast(0);
#if DEBUG
	status = smg$set_term_characteristics( &pasteboard_id, 0, 0, &noecho, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
	status = smg$set_term_characteristics( &pasteboard_id, 0, 0, &noecho, &past, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif

	status = smg$create_virtual_keyboard( &keyboard );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* change the term characteristics back and delete the virtual keyboard
 */


void remove_keyboard()
{
 unsigned int noecho = TT$M_NOECHO, past = TT2$M_PASTHRU;

	sys$setast(0);
	status = smg$delete_virtual_keyboard( &keyboard );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

#if DEBUG
	status = smg$set_term_characteristics( &pasteboard_id, &noecho, 0, 0, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
	status = smg$set_term_characteristics( &pasteboard_id, &noecho, &past, 0, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
	sys$setast(1);
}





/* read a string using the SMG$ routine.  string can be NO LONGER than maxlen
 */


void read_string_smg( char *string, int maxlen  )
{
 struct dsc$descriptor_d d_string,def_string;	/* d_string is a dymanic descriptor */
 vax$u_word return_length;              /* return length of the string read in */
 int    modifiers;


/* used to NOT echo the terminator and disable line recall */

 	modifiers = TRM$M_TM_TRMNOECHO + TRM$M_TM_NORECALL;

	d_string.dsc$w_length  = maxlen;                /* max length of string */
 	d_string.dsc$a_pointer = string;		/* address of string */
 	d_string.dsc$b_class   = DSC$K_CLASS_S;		/* string descriptor class */
 	d_string.dsc$b_dtype   = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = smg$read_string( &keyboard, &d_string, 0, &maxlen, &modifiers, 0, 0,
        	   	 	  &return_length, 0, &current_display );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

 	string[return_length] = '\0'; 		/* put a C string terminator in it */
}





/* title the CONTROL virtual display with the Menu options.
 */


void title()
{
 int y = 1, x = 80;

	sys$setast(0);
	current_display = control_id;

	rendition = 0;
	puts_smg( " dj  ack  ut  rz  lp  eys  og  ov  ew  aste pr nt  frsh  w  ask  rite ma   uit ", 1, 1, FALSE );
	rendition = SMG$M_BOLD;
	puts_smg( "A",  1,  1, FALSE );
	puts_smg( "B",  5,  1, FALSE );
	puts_smg( "C", 10,  1, FALSE );
	puts_smg( "F", 14,  1, FALSE );
	puts_smg( "H", 18,  1, FALSE );
	puts_smg( "K", 22,  1, FALSE );
	puts_smg( "L", 27,  1, FALSE );
	puts_smg( "M", 31,  1, FALSE );
	puts_smg( "N", 35,  1, FALSE );
	puts_smg( "P", 39,  1, FALSE );
	puts_smg( "I", 47,  1, FALSE );
	puts_smg( "R", 51,  1, FALSE );
	puts_smg( "S", 57,  1, FALSE );
	puts_smg( "T", 60,  1, FALSE );
	puts_smg( "W", 65,  1, FALSE );
	puts_smg( "X", 73,  1, FALSE );
	puts_smg( "Q", 75,  1, FALSE );
	smg$set_cursor_abs( &current_display, &y, &x );
	rendition = 0;
	sys$setast(1);
}





/* Let's set everything up!
 */


void init_everything()
{
 struct dsc$descriptor_d d_string;
 struct dsc$descriptor_d d_mbxnam;
 struct dsc$descriptor_d d_lognam;
 int i,
     rows,	/* rows and cols for CONTROL display */
     cols;
 vax$u_longword	mask = 34603016,	/* bits 8, 20, 25 for ctrl c, t, y */
		bordered = SMG$M_BORDER,	/* attributes and characteristics of CONTROL display */
		attributes = SMG$M_REVERSE,
		c_attrib = SMG$M_UNDERLINE;
 char help[] = "Help";
 char task[] = "Select Task",
      mbxname[15],
      username[13];


	for(i=0; i<MAXWIN; i++) task_ids[i]='-';

/* Set this up for puts_smg calls	*/

 	puts_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
 	puts_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */


	d_string.dsc$w_length = strlen( bintime_s );	/* length of name */
	d_string.dsc$a_pointer = bintime_s;		/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = sys$bintim( &d_string, &bintime );		/* convert the paste delay time to binary 	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	d_string.dsc$w_length = strlen( second_s );	/* length of name */
	d_string.dsc$a_pointer = second_s;		/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = sys$bintim( &d_string, &second );		/* convert the paste delay time to binary 	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = sys$dclexh (&exit_block_descrip);		/* set up the exit handler	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	
	status = lib$get_ef( &control_efn );			/* get the CONTROL event flag	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = lib$get_ef( &wait_efn );			/* get the timer event flag	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = lib$get_ef( &paste_efn );			/* get the paste event flag	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = lib$get_ef( &xon_efn );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = sys$clref( xon_efn );
	if (status == SS$_ILLEFC) lib$signal(status);

	status = sys$clref( control_efn );			/* clear control event flag	*/
	if (status == SS$_ILLEFC) lib$signal(status);

	status = sys$clref( paste_efn );			/* clear paste event flag	*/
	if (status == SS$_ILLEFC) lib$signal(status);

	win = -1;
	lwin = -1;

	status = sys$assign( &kbd_devnam, &kbd_chan, 0, 0 );	/* assign channel to user's terminal	*/
	if ( status != SS$_NORMAL ) lib$signal( status );

/* Now read the terminal characteristics so we can alter them */

	status = sys$qiow( 0, kbd_chan, IO$_SENSEMODE, &kbd_iosb, 0, 0, &saved_char, 12, 0, 0, 0, 0);
	
	if (status & SS$_NORMAL)
	{
		if (kbd_iosb.status & SS$_NORMAL)
		{
			changed_char = saved_char;
			changed_char.basic_char = TT$M_NOECHO | TT$M_NOBRDCST | changed_char.basic_char;
#if DEBUG
			changed_char.extended_char = /*TT2$M_PASTHRU | */ changed_char.extended_char;
#else
			changed_char.extended_char = TT2$M_PASTHRU | changed_char.extended_char;
#endif
		}
		else lib$signal(kbd_iosb.status);
	}
	else lib$signal(status);

/* Change the terminal characteristics.	*/

	status = sys$qiow( 0, kbd_chan, IO$_SETMODE, &kbd_iosb, 0, 0, &changed_char, 12, 0, 0, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	if (!(kbd_iosb.status & SS$_NORMAL)) lib$signal(kbd_iosb.status);

	status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadoff[0], 2, 0, 0, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* Setup the pasteboard and determine users's terminal height and width	*/

	sys$setast(0);
	status = smg$create_pasteboard( &pasteboard_id, 0, &pasteboard_rows, &pasteboard_cols, 0, &pasteboard_termtype );
	if ( status != SS$_NORMAL ) lib$signal( status );
	sys$setast(1);

	if ( pasteboard_termtype != SMG$K_VTTERMTABLE )
	{
		printf("\n\nYou must use a VT compatible terminal with this program.\n\n");
		exit(0);
	}

	sys$setast(0);
   	smg$set_out_of_band_asts( &pasteboard_id, &mask, &get_out_of_band_asts );

   	smg$set_broadcast_trapping( &pasteboard_id, &get_broadcast );

/* Create control window for SWiM */

	rows = 1;  cols = pasteboard_cols;
	status = smg$create_virtual_display( &rows, &cols, &control_id, 0, &c_attrib );
	if ( status != SS$_NORMAL ) lib$signal( status );
	sys$setast(1);

	title();


/* Create task list window for SWiM */

	rows = total_tasks;  cols = 20;
	attributes = 0;
	sys$setast(0);
	status = smg$create_virtual_display( &rows, &cols, &task_id, &bordered, &attributes );
	if ( status != SS$_NORMAL ) lib$signal( status );

	d_string.dsc$w_length = strlen( task );		/* length of name */
	d_string.dsc$a_pointer = task;			/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	rendition = SMG$M_BOLD;
	status = smg$label_border( &task_id, &d_string, 0, 0, &rendition, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	rendition = 0;

/* Center the error window in the pasteboard	*/

	error_x = pasteboard_cols/2 - 30;
	error_y = pasteboard_rows/2 - 5;
	attributes = 0;
	rows = 10;  cols = 60;
	status = smg$create_virtual_display( &rows, &cols, &error_id, &bordered, &attributes );
	if ( status != SS$_NORMAL ) lib$signal( status );


/* Center the title window in the pasteboard	*/

	title_x = pasteboard_cols/2 - 15;
	title_y = pasteboard_rows/2 - 7;
	attributes = 0;
	rows = 14;  cols = 30;
	status = smg$create_virtual_display( &rows, &cols, &title_id, &bordered, &attributes );
	if ( status != SS$_NORMAL ) lib$signal( status );

/* Center the help window in the pasteboard	*/

	help_x = pasteboard_cols/2 - 40;
	if ( help_x == 0 ) help_x = 1;
	help_y = pasteboard_rows/2 - 12;
	if ( help_y == 0 ) help_y = 1;
	attributes = 0;
	rows = 24;  cols = 80;
	status = smg$create_virtual_display( &rows, &cols, &help_id, &bordered, &attributes );
	if ( status != SS$_NORMAL ) lib$signal( status );

	d_string.dsc$w_length = strlen( help );		/* length of name */
	d_string.dsc$a_pointer = help;			/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = smg$label_border( &help_id, &d_string, 0, 0, &rendition, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

#if DEBUG

	attributes = 0;
	status = smg$create_virtual_display( &data_rows, &data_cols, &data_id, &bordered, &attributes );
	if ( status != SS$_NORMAL ) lib$signal( status );

#endif
	sys$setast(1);

/* Initialize the window structs, etc.	*/

	for( i=0; i<MAXWIN; i++ )
	{
		window[i].active = FALSE;
		escmode[i]	 = FALSE;
		autologout[i]	 = FALSE;
	}
	cut_width = -1;
	cut_height = -1;
}







/* Simple exit handler routine to reset terminal characteristics */


exit_handler()
{
 int		status;
 struct	iosb	exit_iosb;

	status = sys$qiow( 0, kbd_chan, IO$_SETMODE, &kbd_iosb, 0, 0, &saved_char, 12, 0, 0, 0, 0);
	if (!(status & SS$_NORMAL)) printf("Error resetting terminal reason is %9X");
}





/* This routine will signal when an XOFF AST is delivered.  It then resets the xoff ast call, saves the data in a buffer
 * and suspends all further output to a PT.  Output is resumed when an XON is received from the PT
 */


xoff_ast(int bwin)
{
 int win,
     save_disp;
#if DEBUG
 char temp[80];
#endif

	win = bwin & 0xffff;
#if FTA
	sys$clrast();
#endif
#if DEBUG
	if ( monitor )
	{
		save_disp = current_display;
		current_display = data_id;
		sprintf( temp, "XOFF received from PT.   %d/%d chars actually sent", ww_iosb[win].byte_cnt, ww_len[win] );
		sys$setast(0);
		puts_nl_smg( temp, 1, mony, FALSE );
		sys$setast(1);
		up_mony();
		current_display = save_disp;
	}
#endif

	window[win].frozen = TRUE;

	memset( xoff_buff, 0, sizeof(xoff_buff));

	strncpy( xoff_buff, &kbd_r_buff[ww_iosb[win].byte_cnt], ww_len[win]-ww_iosb[win].byte_cnt );

#if FTA
{
   /* nothing required, AST is repeating */
}
#else
	status = sys$qio ( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, &xoff_ast, win, 0, 2, 0, 0);
#endif

	status = sys$clref( xon_efn );
	if (status == SS$_ILLEFC) lib$signal(status);

     	control_key = DATAOVERUN;
	sys$setef( control_efn );
}






/* This routine will signal when an XON AST is delivered it resets AST and sets the XON event flag
 */


xon_ast( int bwin )
{
 int win,
     save_disp;

#if DEBUG
	if ( monitor )
	{
		save_disp = current_display;
		current_display = data_id;
		sys$setast(0);
		puts_nl_smg( "XON received from PT.", 1, mony, FALSE );
		sys$setast(1);
		up_mony();
		current_display = save_disp;
	}
#endif

	win = bwin & 0xffff;
#if FTA
	sys$clrast();
#endif
	status = sys$setef( xon_efn );
	if (status == SS$_ILLEFC) lib$signal(status);

#if FTA
{
   /* nothing required, AST is repeating */
}
#else
	status = sys$qio ( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, &xon_ast, win, 0, 1, 0, 0);
#endif

	window[win].frozen = FALSE;
}






/* Routine to handle message received on termination mailbox.
 */


void read_mbx_ast( int rwin )
{
 int i;

#if FTA
	sys$clrast();
#endif
	if (!autologout[rwin])	/* if no autologout, then restart read on termination mailbox.	*/
	{
        	status = sys$qio (0, mbx_chan[win], IO$_READVBLK, &mbx_iosb[win], read_mbx_ast, win, &mbx_buff[win][0],
			  sizeof(mbx_buff[win]), 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		return;
	}
	

#if FTA
        status = ptd$delete( w_chan[rwin] );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
	status = sys$dassgn( w_chan[rwin] );			/* deassign channel to pt device	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif

	status = sys$dassgn( mbx_chan[rwin] );			/* and to termination mailbox		*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	window[rwin].active = FALSE;
	sys$setast(0);
	status = smg$delete_virtual_display( &window[rwin].disp_id );	/* ZAP the window!	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);

	i=0;								/* Switch to next available window	*/
	while ( (!window[i].active) && (i<MAXWIN) ) i++;

	if ( i==MAXWIN )
	{
		sys$setast(0);
		status = smg$erase_pasteboard( &pasteboard_id );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		sys$setast(1);
		printf("\nNo more SWiM terminals available.\n\n");
		exit(0);
	}

	win=i;

/* When switching to a new window, make sure user's terminal's keypad state is right for the session */

	if (( window[win].keypad ) && (!keypad_is_on))
	{
		status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadon[0], 2, 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		keypad_is_on = TRUE;
	}
	if ((!window[win].keypad) && (keypad_is_on))
	{
	        status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadoff[0], 2, 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		keypad_is_on = FALSE;
	}

	sys$setast(0);
	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);

	current_display = window[win].disp_id;

	cursor( window[win].c_x, window[win].c_y );

}





/* Log data coming from pseudo terminal to a file.  EVERYTHING is saved	*/


void log_data_full( int win )
{
 int bytes, i = 0;

	sys$setast( 0 );	/* Disable ASTs...needed for RMS$ calls	*/

	bytes = wr_iosb[win].byte_cnt;

	fwrite( wr_buff[win], bytes, 1, win_log[win] );		/* write everything in a raw format	*/

	sys$setast( 1 );
}





/* Log data coming from a pseudo terminal to a file.  Most control chars and all ESC sequences are stripped.	*/


void log_data_stripped( int win )
{
 int bytes, i = 0, last_io = 0;
 char ch;


	sys$setast( 0 );	/* Disable ASTs...needed for RMS$ calls	*/
	bytes = wr_iosb[win].byte_cnt-1;
	while ( (i < bytes) && (last_io != EOF) )
	{
		if ( (ch = wr_buff[win][i])<'\x20' )
		{
			switch ( ch )
			{
			case '\x9'	: last_io = fputc( ch, win_log[win] );
					  lf += 8;
					  break;
			case '\xa'	: last_io = fputc( ch, win_log[win] );
					  lf = 0;
					  break;
			case '\xb'	: last_io = fputc( ch, win_log[win] );
					  lf = 0;
					  break;
			case '\xc'	: last_io = fputc( ch, win_log[win] );
					  lf = 0;
					  break;
			case '\xd'	: last_io = fputc( ch, win_log[win] );
					  lf++;
					  break;
			}
		}
		else
		{
			lf++;
			last_io = fputc( ch, win_log[win] );
		}
		i++;
		if ( lf > 131 )
		{
			last_io = fputc( '\xa', win_log[win] );
			lf = 0;
		}
	}
	sys$setast( 1 );
}





/* Unhook the channel AFTER the qio set_mode function has completed	*/

void undo_chan( short int tchan )
{
	if (!(twset_iosb.status & SS$_NORMAL)) lib$signal(twset_iosb.status);

	status = sys$dassgn( tchan );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
}





/* Adjust the TWA device's terminal characteristics to reflect a change in width or height.
 *
 * NOTE:  The SHARE priviledge IS required for the proper operation of this procedure.
 *	  SHARE is required to open a channel to a device that is owned by a different
 *	  process.  Once the channel is open to the TWAn device, we can alter the page
 *	  and width sizes using a QIO IO$_SETMODE function.  Once the operation is complete
 *	  UNDO_CHAN is called to deassign the channel to the TWAn device.  It will not be
 *	  needed again unless the user resizes the window again.
 *
 *	  If you plan on using this feature of SWiM, set the #define PRIVS directive in
 *	  TYPES.H to 1.
 */


void adjust_term_characteristics( int win, long int page, long int width )
{
 struct dsc$descriptor_d d_twname;	/* d_string is a dynamic descriptor */
 char twname[80];
 struct iosb pt_iosb;
 static struct	devchar	achanged_char;		/* new terminal characteristics	*/
 static struct	devchar	asaved_char;		/* original terminal characteristics	*/
 unsigned int basic;
 unsigned short int wwidth;
 unsigned short int ppage;
 short int tchan;


#if PRIVS
	status = sys$getdviw( 0, w_chan[win], 0, &dvi_stuff, 0, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#if FTA
	sprintf( twname, "FTA%d:", dev_depend );
#else
	sprintf( twname, "TWA%d:", dev_depend );
#endif

	d_twname.dsc$w_length = strlen( twname );	/* length of name */
	d_twname.dsc$a_pointer = twname;			/* address of string */
	d_twname.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_twname.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = sys$assign( &d_twname, &tchan, 0, 0 );
	if ( status != SS$_NORMAL ) lib$signal( status );

	wwidth = width;
	ppage = page - BACKSCROLLLINES;

	status = sys$qiow( 0, tchan, IO$_SENSEMODE, &pt_iosb, 0, 0, &asaved_char, 12, 0, 0, 0, 0);
	
	if (status & SS$_NORMAL)
	{
		if (pt_iosb.status & SS$_NORMAL)
		{
			achanged_char = asaved_char;
			achanged_char.buffer_size = wwidth;
			basic = achanged_char.basic_char & 0x00ffffff;
			basic = (page<<24) | basic;
			achanged_char.basic_char = basic;
		}
		else lib$signal(pt_iosb.status);
	}
	else lib$signal(status);

	status = sys$qio ( 0, tchan, IO$_SETMODE, &twset_iosb, undo_chan, tchan, &achanged_char, 12, 0, 0, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
}




/* This is it:	a VT100 terminal in software (again)
 *
 * VT100's are THEE most emulated terminals in existance.  I've had the good fortune of having one on hand to test
 * all the codes.  I haven't implemented ALL the vt100 escape sequences, but the majority are there and it works!
 */


void w_read_ast( int rwin )
{
vax$u_longword	direction,
		count,
		x,
		y,
		srow,
		scol,
		top,
		bot;
int		i, j, k, done, real_y;
char		temp[255], prefix[255], ch, ch8;

window_type	twin;	/* temporary window struct - used to avoid using 'window[rwin]' throughout the code */


#if FTA
	sys$setast(1);
#endif

	twin = window[rwin];	/* get current window information */

	if ( twin.logging == 1 ) log_data_full( rwin );
	if ( twin.logging == 2 ) log_data_stripped( rwin );

	current_display = twin.disp_id;
        rendition = twin.video_attributes;

#if FTA
	memcpy(&wr_iosb[rwin],(ftadev[rwin].rbuff),4);
        memcpy(&wr_buff[rwin],(ftadev[rwin].rbuff->data),wr_iosb[rwin].byte_cnt);
#endif
	if (!(wr_iosb[rwin].status & SS$_NORMAL))	/* Check status of read from pt	*/
	{
		if ( !twin.active )		/* PT has been deleted	*/
		{
			return;
		}
		printf("\n\nError reading from PY device.\n\n");
		lib$signal(wr_iosb[rwin].status);
	}

	j = 0;

#if DEBUG
	if ( monitor )
	{
		current_display = data_id;
		sys$setast(0);
		puts_nl_smg( "Start of data.", 1, mony, FALSE );
		sys$setast(1);
		up_mony();
		monx = 1;
		for( i=0; i<wr_iosb[rwin].byte_cnt; i++ )
		{
		   sprintf( temp, "%2X  ", wr_buff[rwin][i] );
		   if (monx++ == 8 )
		   {
			sys$setast(0);
			puts_nl_smg( temp, monx*4, mony, FALSE  );
			sys$setast(1);
			monx = 0;
			up_mony();
		   }
		   else
		   {
			sys$setast(0);
			puts_smg( temp, monx*4, mony, FALSE );
			sys$setast(1);
		   }
		}
		temp[1]='\0';
		current_display = twin.disp_id;
	}
#endif

	for (i=0; i<wr_iosb[rwin].byte_cnt; i++ )	/* Read each character in turn	*/
	{
		ch8 = wr_buff[rwin][i];
		ch = wr_buff[rwin][i] & '\x7f';		/* Strip the high bit off the character	*/

		if ((!escmode[rwin]) && (ch > '\x1f'))	/* Quick.  printable character?	*/
		{
			prefix[j++] = ch8;
		}
		else if ( escmode[rwin] == 1 )		/* Escape character was the last character.  Determine next step */
		{
                	for ( k = 0; k<9; k++ )
			 twin.params[k] = 0;	/* Clear all escape sequence parameters	*/
                	twin.p_num = 0;
			switch (ch)
			{
/* We're a VT100! */
			case 'Z' :
#if FTA
					memcpy(ftadev[rwin].wbuff->data,&ansiterm[0],7);
					status = ptd$write(w_chan[rwin],0,0,ftadev[rwin].wbuff,7,0,0);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					memcpy(&ww_iosb[rwin],ftadev[rwin].wbuff,4);
#else
				 	status = sys$qiow( 0, w_chan[rwin], IO$_WRITEVBLK, 0, 0, 0,
		      				&ansiterm[0], 7, 0, 0, 0, 0);
#endif
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					escmode[rwin] = FALSE;
					break;

/* Cursor down	*/

			case 'D' : 	bot = twin.scroll_bot;
					if ( twin.c_y == bot )
					{
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display );
						sys$setast(1);
					}
				 	else twin.c_y++;
					if ( twin.c_y>twin.v_rows) twin.c_y = twin.v_rows;
					escmode[rwin] = FALSE;
					break;

/* Newline	*/

			case 'E' :	bot = twin.scroll_bot;
					if ( twin.c_y == bot )
					{
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display );
						sys$setast(1);
					}
					else twin.c_y++;
					twin.c_x = 1;
					escmode[rwin] = FALSE;
					break;

/* Cursor up	*/

			case 'M' :	top = twin.scroll_top;
					if (( twin.c_y==top ) || ( twin.c_y==1) )
					{
						if (twin.scroll_top == 1)
                                                {
                                                	srow = twin.scroll_top;
							scol = twin.scroll_bot;
							srow += BACKSCROLLLINES;
							scol += BACKSCROLLLINES;
							sys$setast(0);
                                        		smg$set_display_scroll_region( &current_display, &srow, &scol );
							sys$setast(1);
                                                }
                                        	direction = SMG$M_DOWN;
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display, 0,0,0,0, &direction );
						sys$setast(1);
						if ( twin.c_y<twin.v_rows ) twin.c_y++;
						if (twin.scroll_top == 1)
                                                {
                                                	srow = twin.scroll_top;
							scol = twin.scroll_bot;
							if (srow > 1) srow += BACKSCROLLLINES;
							scol += BACKSCROLLLINES;
							sys$setast(0);
                                        		smg$set_display_scroll_region( &current_display, &srow, &scol );
							sys$setast(1);
                                                }
                                        }
				 	twin.c_y--;
					if ( twin.c_y<1 ) twin.c_y = 1;
                                        escmode[rwin] = FALSE;
                                        break;

/* Save cursor position, renditon, char set	*/

			case '7' :	twin.save_c_x = twin.c_x;
					twin.save_c_y = twin.c_y;
                                        twin.save_video_attributes = twin.video_attributes;
                                        twin.save_char_set = twin.char_set;
                                        escmode[rwin] = FALSE;
					break;
/* Restore the above	*/

			case '8' : 	twin.c_x = twin.save_c_x;
					twin.c_y = twin.save_c_y;
                                        twin.video_attributes = twin.save_video_attributes;
                                        twin.char_set = twin.save_char_set;
                                        escmode[rwin] = FALSE;
					break;

/* CSI introducer - next mode	*/

                        case '[' : 	escmode[rwin] = 2;
                        		break;

/* Character set g0 change mode	*/

			case '(' :	escmode[rwin] = 3;
					break;

/* Character set g1 change mode	*/

			case ')' :	escmode[rwin] = 4;
					break;

/* Change character height and width - currently unsupported	*/

			case '#' :	escmode[rwin] = 5;
					break;

/* Put keypad in application mode	*/

			case '=' :	twin.keypad = TRUE;
					status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadon[0], 2, 0, 0, 0, 0);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					keypad_is_on = TRUE;
					escmode[rwin] = FALSE;
                                        break;

/* Put keypad in numeric mode	*/

			case '>' :	twin.keypad = FALSE;
					status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadoff[0], 2, 0, 0, 0, 0);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					keypad_is_on = FALSE;
					escmode[rwin] = FALSE;
                                        break;
			case '\x1b' :	escmode[rwin] = 1;
					break;
			default : 	escmode[rwin] = FALSE;
                                        break;
			}
		}
                else if ( escmode[rwin] == 2 )
                {
			if ( ch=='?' )			/* is it a set mode function?	*/
                         escmode[rwin]=7;
			else if ( ch=='\x1b' )
			 escmode[rwin] = 1;
			else
			{
				i--;
				escmode[rwin]=9;	/* No, go get some parameters	*/
			}
		}
		else if ( escmode[rwin]==7 )		/* get a number or two	*/
		{
                	done = TRUE;
        	        while ((done) && (i<wr_iosb[rwin].byte_cnt))
	                {
                        	ch = wr_buff[rwin][i];
                	        if ( (ch>='0') && (ch<='9') )
        	                {
				        twin.params[twin.p_num] = 
						twin.params[twin.p_num] * 10 + ( ch-'0' );
                        		i++;
                	        }
        	                else if ( ch == ';' )
	                        {
                                	twin.p_num++;
                        	        i++;
                	        }
        	                else if ( ch == '\x1b' )
				{
					escmode[rwin] = 1;
					done = FALSE;
				}
				else
				{
					done = FALSE;
					escmode[rwin] = 8;
					i--;
				}
	                }
		}
		else if ( escmode[rwin]==9 )		/* get a number or two or ...	*/
		{
			done = TRUE;
                        while ((done) && (i<wr_iosb[rwin].byte_cnt))
                        {
                        	ch = wr_buff[rwin][i];
                                if ( (ch>='0') && (ch<='9') )
                                {
	                            	twin.params[twin.p_num] = 
						twin.params[twin.p_num] * 10 + ( ch-'0' );
                                        i++;
                                }
                                else if ( ch == ';' )
                                {
                                	twin.p_num++;
                                        i++;
                                }
        	                else if ( ch == '\x1b' )
				{
					escmode[rwin] = 1;
					done = FALSE;
				}
                                else
				{
					done = FALSE;
					escmode[rwin]=6;
					i--;
				}
                        }
		}
		else if ( escmode[rwin]==6 )	/* CSI mode...after ESC[	*/
		{
			ch = wr_buff[rwin][i];
                        switch (ch)
                        {

/* Erase chars	*/

                        case 'K' : if (twin.params[0]==1)	/* from beginning of line to cursor	*/
				   {
					count = twin.c_x;
                                        x = 1;
					real_y = twin.c_y + BACKSCROLLLINES;
					sys$setast(0);
					smg$erase_chars( &current_display, &count, &real_y, &x );
					sys$setast(1);
				   }
				   else if (twin.params[0]==2)	/* the whole line	*/
                                   {
					scol = 1;
					real_y = twin.c_y + BACKSCROLLLINES;
					sys$setast(0);
					smg$erase_line( &current_display, &real_y, &scol );
					sys$setast(1);
                                   }
                                   else					/* from cursor to end of line	*/
                                   {
					real_y = twin.c_y + BACKSCROLLLINES;
                                   	count = twin.v_cols - twin.c_x + 1;
					sys$setast(0);
					smg$erase_chars( &current_display, &count, &real_y, &twin.c_x );
					sys$setast(1);
                                   }
                                   break;

/* Erase a lot of chars	*/
/* NOTE:  I call smg$move_virtual_display to get around a bug in smg$erase_display - SEE DOCS	*/

			case 'J' : if (twin.params[0]==1)	/* from beginning of screen to cursor	*/
				   {
                                   	srow = BACKSCROLLLINES+1;
                                        scol = 1;
					real_y = twin.c_y + BACKSCROLLLINES;
					sys$setast(0);
					smg$erase_display( &current_display, &srow, &scol, &real_y, &twin.c_x );
					smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x );
					sys$setast(1);
                                   }
                                   else if (twin.params[0] == 2 )	/* Entire screen	*/
                                   {
                                   	srow = BACKSCROLLLINES+1;
                                        scol = 1;
					real_y = twin.p_rows;
					sys$setast(0);
					smg$erase_display( &current_display, &srow, &scol, &real_y, &twin.p_cols );
					smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x );
					sys$setast(1);
                                   }
                                   else					/* from cursor to end of screen	*/
                                   {
					srow = twin.p_rows;
					scol = twin.p_cols;
					real_y = twin.c_y + BACKSCROLLLINES;
					sys$setast(0);
					smg$erase_display( &current_display, &real_y, &twin.c_x, &srow, &scol );
					smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x );
					sys$setast(1);
                                   }
                                   break;

/* Move cursor to row, column	*/

                        case 'H' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   if ( twin.params[1]==0 ) twin.params[1]=1;
				   if (twin.omode )
				   {
					twin.c_y = twin.params[0]+twin.scroll_top;
                        	   	twin.c_x = twin.params[1];
				   }
                                   else
                                   {
					twin.c_y = twin.params[0];
                        	   	twin.c_x = twin.params[1];
                                   }
                                   break;

/* Move cursor to row, column	*/

                        case 'f' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   if ( twin.params[1]==0 ) twin.params[1]=1;
				   if (twin.omode )
				   {
					twin.c_y = twin.params[0]+twin.scroll_top;
                        	   	twin.c_x = twin.params[1];
				   }
                                   else
                                   {
					twin.c_y = twin.params[0];
                        	   	twin.c_x = twin.params[1];
                                   }
                                   break;

/* Cursor up	*/

			case 'A' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   if ( twin.c_y != twin.scroll_top )
				    twin.c_y -= twin.params[0];
				   if (twin.c_y<1) twin.c_y=1;
                                   break;

/* Cursor down	*/

			case 'B' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   if ( twin.c_y != twin.scroll_bot )
				    twin.c_y += twin.params[0];
        	        	   if ( twin.c_y > twin.v_rows ) twin.c_y = twin.v_rows;
                                   break;

/* Cursor right	*/

			case 'C' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   twin.c_x += twin.params[0];
				   if (twin.c_x > twin.v_cols ) twin.c_x = twin.v_cols;
                                   break;

/* Cursor left	*/

			case 'D' : if ( twin.params[0]==0 ) twin.params[0]=1;
				   twin.c_x -= twin.params[0];
				   if (twin.c_x < 1 ) twin.c_x = 1;
                                   break;

/* Delete line	*/

			case 'M' : if ( twin.params[0]==0 ) twin.params[0] = 1;
				   count = twin.params[0];
				   real_y = twin.c_y + BACKSCROLLLINES;
				   sys$setast(0);
				   smg$delete_line( &current_display, &real_y, &count );
				   sys$setast(1);
                                   break;

/* Insert line	*/

			case 'L' : if ( twin.params[0]==0 ) twin.params[0] = 1;
				   count = twin.params[0];
				   direction = SMG$M_DOWN;
                                   for (k=0; k<count; k++)
				   {
					   real_y = twin.c_y + BACKSCROLLLINES;
					   sys$setast(0);
					   smg$insert_line( &current_display, &real_y, 0, &direction );
					   sys$setast(1);
                                   }
                                   break;

/* Erase number of characters - NOT a vt100 function, but included here so I could play VAX TETRIS (which uses this function) */

			case 'X' : if ( twin.params[0]==0 ) twin.params[0] = 1;
				   count = twin.params[0];
				   real_y = twin.c_y + BACKSCROLLLINES;
				   sys$setast(0);
				   smg$erase_chars( &current_display, &count, &real_y, &twin.c_x );
				   sys$setast(1);
                                   break;

/* Enter insertion mode	*/

			case 'h' : if ( twin.params[0]==4 ) twin.insert = TRUE;
                                   break;

/* Exit insertion mode = enter replacement mode	*/

			case 'l' : if ( twin.params[0]==4 ) twin.insert = FALSE;
                                   break;

/* Set character attributes	*/

			case 'm' : for (k=0; k<twin.p_num+1; k++ )
				    switch (twin.params[k])
				    {
                                    	case 0 : twin.video_attributes = 0;
                                                 break;
                                        case 1 : twin.video_attributes |= SMG$M_BOLD;
                                        	 break;
                                        case 4 : twin.video_attributes |= SMG$M_UNDERLINE;
                                        	 break;
                                        case 5 : twin.video_attributes |= SMG$M_BLINK;
                                        	 break;
                                        case 7 : twin.video_attributes |= SMG$M_REVERSE;
                                        	 break;
                                    }
                                   rendition = twin.video_attributes;
				   break;

/* Set scrolling margins	*/

			case 'r' : if (( twin.params[0]==0 ) && (twin.params[1]==0))
				   {
					twin.scroll_top = 1;
					twin.scroll_bot = twin.v_rows;
					sys$setast(0);
					smg$set_display_scroll_region( &current_display );
					sys$setast(1);
					twin.c_x = 1;
                                        twin.c_y = 1;
				   }
				   else
				   {
                                   	srow = twin.params[0];
					if ( srow==0 ) srow = 1;
                                        scol = twin.params[1];
					if ( scol==0 ) scol = twin.v_rows;
					twin.scroll_top = srow;
					twin.scroll_bot = scol;
					if (srow > 1) srow += BACKSCROLLLINES;
					scol += BACKSCROLLLINES;
					sys$setast(0);
                                        smg$set_display_scroll_region( &current_display, &srow, &scol );
					sys$setast(1);
				        if (twin.omode )
				        {
						twin.c_y = twin.scroll_top;
                        	   		twin.c_x = 1;
				        }
                                        else
                                        {
						twin.c_y = 1;
                        	   		twin.c_x = 1;
                                        }
				   }
				   break;
                        }
                        if ( ch=='\x1b')
			 escmode[rwin] = 1;
			else escmode[rwin] = FALSE;
                }
		else if ( escmode[rwin]==3 )	/* turn on/off graphics characters - no other chars supported	*/
		{
			done = TRUE;
			switch (ch)
			{
        	        case '\x1b' :	escmode[rwin] = 1;
					done = FALSE;
					break;
                        case 'B' :	twin.g1 = FALSE;
                        		break;
                        case '0' :	twin.g1 = TRUE;
                        		break;
                        }
                        if ( done ) escmode[rwin] = FALSE;
		}
		else if ( escmode[rwin]==4 )	/* ingore	*/
		{
			if ( ch == '\x1b' )
			 escmode[rwin] = 1;
			else escmode[rwin] = FALSE;
		}
		else if ( escmode[rwin]==5 )	/* currently unsupported but WILL be in the future	*/
		{
			done = TRUE;
			switch (ch)
                        {
        	        case '\x1b' :	escmode[rwin] = 1;
					done = FALSE;
					break;
                        case '3' : 	twin.highwide = TRUE;
					twin.bottom = FALSE;
                                        break;
                        case '4' : 	twin.highwide = TRUE;
					twin.bottom = TRUE;
                                        break;
			case '5' :	twin.highwide = FALSE;
					twin.bottom = FALSE;
					twin.wide = FALSE;
                                        break;
			case '6' :      twin.wide = TRUE;
					twin.bottom = FALSE;
                                        twin.highwide = FALSE;
					break;
                        }
                        if ( done ) escmode[rwin] = FALSE;
                }
                else if ( escmode[rwin]==8 )	/* set mode mode  ESC[?	*/
		{
			done = TRUE;
			if ( ch == '\x1b' )
			{
				escmode[rwin] = 1;
				done = FALSE;
			}
			else
                	switch (twin.params[0])
                        {
			case 3	: if ( ch=='l' )	/* set 132/80 columns	*/
				  {
                                        scol = 80;
					twin.p_cols = 80;
					twin.v_cols = 80;
					twin.save_cols = 80;
					sys$setast(0);
                                   	smg$change_virtual_display( &current_display, 0, &scol );
                                   	smg$change_viewport( &current_display, 0, 0, 0, &scol );
					sys$setast(1);
                                  }
                                  else if ( ch=='h' )
                                  {
                                        scol = 132;
					twin.p_cols = 132;
					twin.v_cols = 132;
					twin.save_cols = 132;
					sys$setast(0);
                                   	smg$change_virtual_display( &current_display, 0, &scol );
                                   	smg$change_viewport( &current_display, 0, 0, 0, &scol );
					sys$setast(1);
                                  }
                                  srow = BACKSCROLLLINES+1;
                                  scol = 1;
				  real_y = twin.p_rows;
				  sys$setast(0);
				  smg$erase_display( &current_display, &srow, &scol, &real_y, &twin.p_cols );
				  smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x );
				  sys$setast(1);
				  adjust_term_characteristics( rwin, twin.v_rows, twin.v_cols );
				  twin.c_x = twin.c_y = 1;
                                  break;

/* set cursor origin mode	*/

                        case 6	: if ( ch=='l' )
                                   twin.omode = FALSE;
                                  else if ( ch=='h' )
				   twin.omode = TRUE;
                                  break;

                        default : break;
                        }
                        if ( done ) escmode[rwin] = FALSE;
		}

/* NOT in an escape sequence and NOT a valid printable character so...it must be a control char	*/

		else
		{
			if ( j>0 )	/* put what we have in the prefix string to the window	*/
			{
				sys$setast(0);
				prefix[j]='\0';
				if ( twin.bottom )
				{
				puts_smg_w( prefix, twin.c_x, twin.c_y-1, twin.g1, twin.insert,
					    twin.wide, twin.highwide );
					sys$setast(1);
					twin.highwide = FALSE;
					twin.bottom = FALSE;
				}
				else puts_smg_w( prefix, twin.c_x, twin.c_y, twin.g1, twin.insert,
					    twin.wide, twin.highwide );
				twin.wide = FALSE;
				twin.c_x += j;
				j=0;
				sys$setast(1);
			}
			switch (ch)
			{
/* Line feed	*/

			case '\xa' :    bot = twin.scroll_bot;
					if ( twin.c_y == bot )
					{
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display );
						sys$setast(1);
					}
					else twin.c_y++;
					break;
			case '\xb' :    bot = twin.scroll_bot;
					if ( twin.c_y == bot )
					{
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display );
						sys$setast(1);
					}
					else twin.c_y++;
					break;
			case '\xc' :    bot = twin.scroll_bot;
					if ( twin.c_y == bot )
					{
						sys$setast(0);
                                        	smg$scroll_display_area( &current_display );
						sys$setast(1);
					}
					else twin.c_y++;
					break;

/* Carriage return	*/

			case '\xd' :	twin.c_x = 1;
					break;

/* Escape!	*/

			case '\x1b' :	escmode[rwin] = 1;
					break;

/* cursor left	*/

			case '\x8' :	if ( twin.c_x > 1 ) twin.c_x--;
					break;

/* BELL - ding ding	*/

			case '\x7' :	sys$setast(0);
					puts_smg_w( "\x7", twin.c_x, twin.c_y, twin.g1, twin.insert,
						 twin.wide, twin.highwide );
					sys$setast(1);
				     	break;

/* tab - hard coded 8 char tabs - a tab table will be supported in the future	*/

			case '\x9' :    twin.c_x = ((((twin.c_x-1) / 8 )*8)+8)+1;
					break;

/* turn on graphics chars	*/

			case '\xe' : twin.g1 = TRUE;
				     break;

/* turn off graphics chars	*/

			case '\xf' : twin.g1 = FALSE;
				     break;
			}
		}
	}
	if ( j>0 )	/* all done...put what we got	*/
	{
		sys$setast(0);
		prefix[j]='\0';
		if ( twin.bottom )
		{
			puts_smg_w( prefix, twin.c_x, twin.c_y-1, twin.g1, twin.insert,
					    twin.wide, twin.highwide );
			twin.highwide = FALSE;
			twin.bottom = FALSE;
		}
		else puts_smg_w( prefix, twin.c_x, twin.c_y, twin.g1, twin.insert,
			    twin.wide, twin.highwide );
		twin.wide = FALSE;
		twin.c_x += j;
		sys$setast(1);
	}

	window[rwin] = twin;	/* update the real window struct */


        current_display = window[win].disp_id;
	real_y = window[win].c_y + BACKSCROLLLINES;
	sys$setast(0);
	smg$set_cursor_abs( &current_display, &real_y, &window[win].c_x );
	sys$setast(1);

#if DEBUG
	if ( monitor )
	{
		current_display = data_id;
		sys$setast(0);
		puts_nl_smg( "", 1, mony, FALSE );
		sys$setast(1);
		up_mony();
		sprintf( temp, "End of data.  sbot = %d  stop = %d  cx = %d  cy = %d", twin.scroll_top,
			       twin.scroll_bot, twin.c_x, twin.c_y );
		sys$setast(0);
		puts_nl_smg( temp, 1, mony, FALSE );
		sys$setast(1);
		up_mony();
		monx = 0;
	}
#endif

/* Reset read on PT device	*/

#if FTA
	status = ptd$read(0,w_chan[rwin],w_read_ast,rwin,ftadev[rwin].rbuff,
                          WRBUFFLEN-1);
	if (!(status & SS$_NORMAL))
	{
		printf("\n\nError reading from FT - program exiting!\n\n");
		lib$signal(status);
	}

#else
	status = sys$qio ( 0, w_chan[rwin], IO$_READVBLK, &wr_iosb[rwin], w_read_ast, rwin,
	                     &wr_buff[rwin], WRBUFFLEN-1, 0, 0, 0, 0);
	
	if (!(status & SS$_NORMAL))
	{
		printf("\n\nError reading from PT - program exiting!\n\n");
		lib$signal(status);
	}
#endif


	if ( paste_win == rwin )	/* Ok to continue paste operation - must be timed precisely	*/
	{
	    	status = sys$setef(paste_efn);
		if (status == SS$_ILLEFC) lib$signal(status);
	}
}




set_mode_ast(int win)
{
	adjust_term_characteristics( win, window[win].v_rows, window[win].v_cols );

/*	status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, &set_mode_ast, win, 0, 3, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
*/
}




/* Create a new SWiM session - Start SWiMming today!	*/


void create_window( int same_user, int x, int y, int rows, int cols )
{
 struct dsc$descriptor_d d_string,
			 d_twname,	/* d_string is a dynamic descriptor */
			 d_command,
			 d_prcnam;
 vax$u_longword	bordered = SMG$M_BORDER,
		rendition = SMG$M_REVERSE;
 int i, io_quota, key, priority, p6,p7;
 unsigned int pid;
 char twname[15],
      mbxname[15],
      title[80],
      command[80],
      quota[80],
      username[13],
      processname[16],
      pid_string[10];
vax$u_longword	stsflg = PRC$M_INTER + PRC$M_DETACH + PRC$M_NOPASSWORD;		/* status flag for $CREPRC	*/

#if FTA
int inadr[2];
#endif


	io_quota = get_quota();

	if ( do_io_quota )
	{
		io_quota_d = io_quota;
	}
	else if ( io_quota < io_quota_d )	/* Must have enough quota!	*/
	{
		setup_error();
		current_display = error_id;
		sys$setast(0);
		puts_nl_smg( "You have insufficient Buffered_IO_Byte_Count_Quota", 1, 2, FALSE );
		puts_nl_smg( "to create another session.  Creation aborted.", 1, 4, FALSE );
		puts_nl_smg( "Press a key.", 16, 9, FALSE );
		smg$read_keystroke( &keyboard, &key );
		sys$setast(1);
		remove_error();
		return;
	}

	if ( get_ast_quota() < 5 )	/* Must have enough quota!	*/
	{
		setup_error();
		current_display = error_id;
		sys$setast(0);
		puts_nl_smg( "You have insufficient remaining AST quota", 1, 2, FALSE );
		puts_nl_smg( "to create another session.  Creation aborted.", 1, 4, FALSE );
		puts_nl_smg( "Press a key.", 16, 9, FALSE );
		smg$read_keystroke( &keyboard, &key );
		sys$setast(1);
		remove_error();
		return;
	}

#if DEBUG
	if (qmonitor)
	{
		current_display = data_id;
		sprintf( quota, "create_window start  buff_io byte count quota = %d", get_quota() );
		if (++qy>24) qy=24;
		qx = 1;
		sys$setast(0);
		puts_nl_smg( quota, qx, qy, FALSE );
		sys$setast(1);
	}
#endif

     	i=0;					/* find an open window table	*/
	while( window[i].active ) i++;
	win = i;
	lwin++;

	window[win].active	     = TRUE;		/* set a few bits here and there	*/
	window[win].same	     = same_user;
	window[win].frozen	     = FALSE;
	window[win].maxed	     = FALSE;
	window[win].logging	     = FALSE;
	window[win].omode	     = FALSE;
	window[win].insert	     = FALSE;
	window[win].c_x		     = 1;
	window[win].c_y		     = 1;
	window[win].p_x		     = x +1;
	window[win].save_x	     = x +1;
	window[win].p_y		     = y +1;
	window[win].save_y	     = y +1;
	window[win].p_rows	     = rows + BACKSCROLLLINES;
	window[win].save_rows	     = rows + BACKSCROLLLINES;
	window[win].p_cols	     = cols;
	window[win].save_cols	     = cols;
	window[win].v_rows	     = rows;
	window[win].v_cols	     = cols;
	window[win].v_offsetx        = 1;
	window[win].v_offsety        = BACKSCROLLLINES+1;
	window[win].scroll_top       = 1;
	window[win].scroll_bot       = rows;
	window[win].video_attributes = 0;
	window[win].char_set	     = SMG$C_ASCII;
	window[win].g1		     = FALSE;
        window[win].highwide         = FALSE;
	window[win].bottom	     = FALSE;
        window[win].wide	     = FALSE;
	window[win].border 	     = TRUE;
	window[win].name[0]          = '\0';
	window[win].p_num	     = 0;
        for ( i= 0; i<9; i++ )
	 window[win].params[i] = 0;

/* Create window for PT */

	sys$setast(0);
	status = smg$create_virtual_display( &window[win].p_rows, &window[win].p_cols, &window[win].disp_id,
					     &bordered, &window[win].video_attributes, &window[win].char_set );
	if ( status != SS$_NORMAL ) lib$signal( status );

/* Create the viewport for the PT virtual display */

	status = smg$create_viewport( &window[win].disp_id, &window[win].v_offsety, &window[win].v_offsetx,
					&window[win].v_rows, &window[win].v_cols );
	if ( status != SS$_NORMAL ) lib$signal( status );

/* and label it	*/

	sprintf( title, "SWiM Term #%d", win+1 );

 	d_string.dsc$w_length = strlen( title );	/* length of name */
 	d_string.dsc$a_pointer = title;			/* address of string */
 	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
 	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
/*	if (!(status & SS$_NORMAL)) lib$signal(status);  */

/* and paste it	*/

	status = smg$paste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x, 0 );
	if ( status != SS$_NORMAL ) lib$signal( status );

	status = smg$set_display_scroll_region( &window[win].disp_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);

/* Create a termination mailbox for the PT */

	status = sys$crembx( 0, &mbx_chan[win], 0, 0, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = sys$getdviw( 0, mbx_chan[win], 0, &mbx_stuff, 0, 0, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	sprintf( mbxname, "MBA%d:", dev_depend );

	d_twname.dsc$w_length = strlen( mbxname );	/* length of name */
	d_twname.dsc$a_pointer = mbxname;		/* address of string */
	d_twname.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_twname.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

#if FTA
/* Create an FTAnnn and establish a connection-freed AST */

	status = lib$get_vm_page(&(3*PGPP),&inadr[0]);  /* get 3 contiguous pages */
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	inadr[1] = inadr[0]+2*PAGESIZE;          /* build the addr range   */
	ftadev[win].rbuff = inadr[0];            /* read buffer            */
	ftadev[win].wbuff = inadr[0]+PAGESIZE;   /* write buffer           */

	status = ptd$create(&w_chan[win],0,0,0,read_mbx_ast,win,0,inadr);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* setup the XON, XOFF and CHARCHANGE ASTs */

	status = ptd$set_event_notification(w_chan[win],xon_ast,win,0,PTD$C_SEND_XON);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = ptd$set_event_notification(w_chan[win],xoff_ast,win,0,PTD$C_SEND_XOFF);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = ptd$set_event_notification(w_chan[win],set_mode_ast,win,0,PTD$C_CHAR_CHANGED);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* post a read to the FTA */

	status = ptd$read(0,w_chan[win],w_read_ast,win,ftadev[win].rbuff,
                          WRBUFFLEN-1);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

#else
/* Assign a channel to the standard PYA0 device and let it make another PT */

	status = sys$assign( &wdevnam, &w_chan[win], 0, &d_twname );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* Set up XON, XOFF ASTs */

	status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, xon_ast, win, 0, 1, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, xoff_ast, win, 0, 2, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, set_mode_ast, win, 0, 3, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* Start read on termination mailbox and PT */

	status = sys$qio ( 0, mbx_chan[win], IO$_READVBLK, &mbx_iosb[win], read_mbx_ast, win, &mbx_buff[win][0],
			  sizeof(mbx_buff[win]), 0, 0, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	
	status = sys$qio ( 0, w_chan[win], IO$_READVBLK, &wr_iosb[win], w_read_ast, win, &wr_buff[win],
			  WRBUFFLEN-1, 0, 0, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif

	current_display = window[win].disp_id;

	if (same_user)
	{
		sys$setast(0);
		puts_smg_w( "Creating interactive process, please wait...", window[win].c_x, window[win].c_y, FALSE, FALSE,
			    FALSE, FALSE );

		sys$setast(1);
		window[win].c_y += 2;

		status = sys$getdviw( 0, w_chan[win], 0, &dvi_stuff, 0, 0, 0, 0 );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

#if FTA
		sprintf( twname, "FTA%d:", dev_depend );
#else
		sprintf( twname, "TWA%d:", dev_depend );
#endif

	 	d_twname.dsc$w_length = strlen( twname );	/* length of name */
	 	d_twname.dsc$a_pointer = twname;		/* address of string */
	 	d_twname.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	 	d_twname.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

		get_username( username );
		pid = get_pid();
		sprintf( pid_string, "%x", pid );

		p7 = strlen( pid_string) - 1;
		p6 = p7 - 1;
		
		if ( task_ids[win] == '-' )
			sprintf( processname, "%s%d%c%c", username, win, pid_string[p6], pid_string[p7] );
		else sprintf( processname, "%s%c%c%c", username, task_ids[win], pid_string[p6], pid_string[p7] );
                
	 	d_prcnam.dsc$w_length = strlen( processname );	/* length of name */
	 	d_prcnam.dsc$a_pointer = processname;		/* address of string */
	 	d_prcnam.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	 	d_prcnam.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

		priority = get_priority();	/* use controlling process's base priority for new session	*/

		status = sys$creprc( 0, &image, &d_twname, &d_twname, 0,0,0,&d_prcnam, priority,0,0, stsflg );	/* MAgiC! */
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
        }
	else
	{
		sys$setast(0);
		puts_smg_w( "Press RETURN to login.", window[win].c_x, window[win].c_y, FALSE, FALSE, FALSE, FALSE );
		sys$setast(1);
		window[win].c_y += 2;
	}

#if DEBUG
	if (qmonitor)
	{
		quota[0]='\0';
		current_display = data_id;
		sprintf( quota, "create_window end    buff_io byte count quota = %d", get_quota() );
		if (++qy>24) qy=24;
		qx = 1;
		sys$setast(0);
		puts_nl_smg( quota, qx, qy, FALSE );
		sys$setast(1);
	}
#endif

	if ( do_io_quota )		/* if this is the first time, determine the quota used to create the session */
	{
		io_quota_d = (io_quota_d - get_quota()) * 2;
		do_io_quota = FALSE;
	}
}





void ww_ast( int win )
{
/*
	if (( ww_iosb[win].status == SS$_DATAOVERUN ) && ( ww_iosb[win].byte_cnt < ww_len[win] ))
	{
     		control_key = DATAOVERUN;
		sys$setef( control_efn );
	}
*/
}




/* Keyboard read AST.  look for hot keys otherwise send key right thru to PT	*/


void kbd_r_ast()
{
 char ch;
 int len;



/* See if any more data exists in the type-ahead buffer */

	status = sys$qiow ( 0, kbd_chan, IO$_READVBLK | IO$M_TIMED, &kbd_iosb, 0, 0, &kbd_r_buff[1],
			     (sizeof(kbd_r_buff)-1), 0, &term_block, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		
	len = 1 + kbd_iosb.byte_cnt;
	
	ch = kbd_r_buff[0];
	if ( ch == hot_key )
	{
		control_key = CONTROL;
		status = sys$setef( control_efn );
	}
	else if ( (ch==backward_key) && (backward_key) )
	{
		control_key = BACKWARD;
		status = sys$setef( control_efn );
	}
	else if ( (ch==forward_key) && (forward_key) )
	{
		control_key = FORWARD;
		status = sys$setef( control_efn );
	}
	else
	{
		if (!window[win].frozen)	/* send the data thru to terminal	*/
		{
			ww_len[win]=len;
#if FTA
			memcpy(ftadev[win].wbuff->data,kbd_r_buff,len);
			status = ptd$write(w_chan[win],0,0,ftadev[win].wbuff,len,0,0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
			status = sys$qio ( 0, w_chan[win], IO$_WRITEVBLK, &ww_iosb[win], /* ww_ast */ 0, /* win */ 0,
						&kbd_r_buff[0], len, 0, 0, 0, 0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
		}
		else
		{
		   sys$setast(0);
		   puts_smg_w( "\x7", window[win].c_x, window[win].c_y, window[win].g1, window[win].insert,
		      window[win].wide, window[win].highwide );
		   sys$setast(1);
		}

		kbd_r_buff[0]='\0';
		kbd_r_buff[1]='\0';

/* Reset read on users's physical terminal	*/

		status = sys$qio ( 0, kbd_chan, IO$_READVBLK, &kbd_iosb, kbd_r_ast, 0, &kbd_r_buff[0],
					   1, 0, &term_block, 0, 0 );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}
}





/* Menu option to setup new SWiM session */


void create_new_term()
{
 short int key;
 int same = FALSE,
     logout = FALSE,
     doit = TRUE;

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Log in under same username (Y/n/Quit) ? ", 1, 1, FALSE );

	smg$read_keystroke( &keyboard, &key );
	sys$setast(1);

	switch ( key )
	{
        	case SMG$K_TRM_UPPERCASE_Y : same = TRUE;
					     break;
                case SMG$K_TRM_LOWERCASE_Y : same = TRUE;
					     break;
        	case SMG$K_TRM_UPPERCASE_Q : doit = FALSE;
					     break;
                case SMG$K_TRM_LOWERCASE_Q : doit = FALSE;
					     break;
                case SMG$K_TRM_CR : same = TRUE;
				    break;
        }

	if ( doit )
	{
		sys$setast(0);
		puts_smg( "Automatically delete window when process logs out (y/N/Quit) ? ", 1, 1, FALSE );
		smg$read_keystroke( &keyboard, &key );
		sys$setast(1);
		switch ( key )
		{
	        	case SMG$K_TRM_UPPERCASE_Y : logout = TRUE;
						     break;
	                case SMG$K_TRM_LOWERCASE_Y : logout = TRUE;
						     break;
	        	case SMG$K_TRM_UPPERCASE_Q : doit = FALSE;
						     break;
        	        case SMG$K_TRM_LOWERCASE_Q : doit = FALSE;
						     break;
	                case SMG$K_TRM_CR : logout = FALSE;
					    break;
	        }
	}

	if ( doit ) if ( lwin<MAXWIN )
	{
		create_window( same, 1, 1, 24, 80 );
		autologout[win] = logout;
	}

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Write a script file of the current window coordinates, sizes, etc.	*/

void write_script()
{
 char filename[41];
 int  i;
 short int key;

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Name of script file to create ? ", 1, 1, FALSE );

	read_string_smg( filename, 40 );

	if ( strlen( filename ) != 0 )
	{
		if ( strchr( filename, '.' ) == 0 ) strcat( filename, ".swm" );

		if ( (script = fopen( filename, "w" )) == NULL )
		{
			status = smg$erase_display( &control_id );
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			puts_smg( "Error opening script file.  Press a key.", 1, 1, FALSE );
			smg$read_keystroke( &keyboard, &key );
		}
		else
		{
			fprintf( script, script_header );
			fprintf( script, "%d %d %d\n", hot_key, forward_key, backward_key );
			for( i=0; i<MAXWIN; i++)
			 if ( window[i].active )
				fprintf( script, "%d %d %d %d %d %d %c\n", window[i].same, window[i].p_x, window[i].p_y,
								     window[i].v_rows, window[i].v_cols, autologout[i],
								     task_ids[i] );
			fclose( script );
		}
	}

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Set or clear the logging of a session	*/

void log_session( int win )
{
 struct dsc$descriptor_d d_string;	/* d_string is a dynamic descriptor */
 char filename[41],
      tempfile[41],
      temp[80],
      stitle[30];
 int i;
 short int key;
 vax$u_longword	rendition = SMG$M_REVERSE;


	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	if ( window[win].logging )
	{
		fclose( win_log[win] );
		window[win].logging = FALSE;

		sprintf( stitle, "SWiM Term #%d", win+1 );

	 	d_string.dsc$w_length = strlen( stitle );	/* length of name */
	 	d_string.dsc$a_pointer = stitle;			/* address of string */

		sys$setast(0);
		status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
		sys$setast(1);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		return;
	}

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	sprintf( filename, "SWiM%d.LOG", win+1 );

	sprintf( temp, "Name of log file (RETURN for %s) ? ", filename );

	puts_smg( temp, 1, 1, FALSE );

	read_string_smg( tempfile, 40 );

	if ( strlen( tempfile ) != 0 ) strcpy( filename, tempfile );

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Log session F)ull or S)tripped or Q)uit ? ", 1, 1, FALSE );

	smg$read_keystroke( &keyboard, &key );
	sys$setast(1);
		
	switch (key)
	{
		case SMG$K_TRM_UPPERCASE_F    : window[win].logging = 1;
						break;
		case SMG$K_TRM_LOWERCASE_F    : window[win].logging = 1;
						break;
		case SMG$K_TRM_UPPERCASE_S    : window[win].logging = 2;
						break;
		case SMG$K_TRM_LOWERCASE_S    : window[win].logging = 2;
						break;
		default			      : window[win].logging = FALSE;
						break;
	}		
	if ( window[win].logging )
	{	
		if ( window[win].logging == 1 )
		 win_log[win] = fopen( filename, "w", "rfm=var", "mrs=512" );
		else win_log[win] = fopen( filename, "w" );
		if ( win_log[win] == NULL )
		{
			sys$setast(0);
			status = smg$erase_display( &control_id );
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			puts_smg( "Error opening log file.  Press a key.", 1, 1, FALSE );
			smg$read_keystroke( &keyboard, &key );
			sys$setast(1);
		}
		else
		{
			sprintf( stitle, "SWiM Term #%d  Logging", win+1 );

		 	d_string.dsc$w_length = strlen( stitle );	/* length of name */
		 	d_string.dsc$a_pointer = stitle;	       	/* address of string */

			sys$setast(0);
			status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			sys$setast(1);
		}
	}
	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Move the virtual display around on the screen */

void move_display( int win )
{
 int done = FALSE;
 short int key;


	sys$setast(0);
	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
						&control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Move using Arrow Keys or I,J,K,M.  Q)uit : ", 1, 1, FALSE );

	current_display = window[win].disp_id;

	while (!done)
	{
		cursor( window[win].c_x, window[win].c_y );
		smg$read_keystroke( &keyboard, &key );
		switch (key)
		{
			case SMG$K_TRM_UPPERCASE_I    : window[win].p_y -= 8;
				      break;
			case SMG$K_TRM_UPPERCASE_J    : window[win].p_x -= 8;
				      break;
			case SMG$K_TRM_UPPERCASE_K    : window[win].p_x += 8;
				      break;
			case SMG$K_TRM_UPPERCASE_M    : window[win].p_y += 8;
				      break;
			case SMG$K_TRM_LOWERCASE_I    : window[win].p_y -= 8;
				      break;
			case SMG$K_TRM_LOWERCASE_J    : window[win].p_x -= 8;
				      break;
			case SMG$K_TRM_LOWERCASE_K    : window[win].p_x += 8;
				      break;
			case SMG$K_TRM_LOWERCASE_M    : window[win].p_y += 8;
				      break;
			case SMG$K_TRM_UP : window[win].p_y -= 1;
				      break;
			case SMG$K_TRM_DOWN : window[win].p_y += 1;
				      break;
			case SMG$K_TRM_RIGHT : window[win].p_x += 1;
				      break;
			case SMG$K_TRM_LEFT : window[win].p_x -= 1;
				      break;
			case SMG$K_TRM_UPPERCASE_Q    : done = TRUE;
				      break;
			case SMG$K_TRM_LOWERCASE_Q    : done = TRUE;
				      break;
			case SMG$K_TRM_CR : done = TRUE;
					    break;
		}
		status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
							&control_id );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Adjust the size of the window.  For optimal operation, install SWiM with the SHARE privilege and set PRIVS to 1 in types.h */


void adjust_size( int win )
{
 int done = FALSE;
 short int key;
 struct dsc$descriptor_d d_string;
 vax$u_longword	rendition = SMG$M_REVERSE;
 char wtitle[80];



	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Adjust size using Arrow Keys or I,J,K,M.  Q)uit : ", 1, 1, FALSE );

	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
						&control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	current_display = window[win].disp_id;

	while (!done)
	{
		sprintf( wtitle, "Page=%d  Width=%d", window[win].v_rows, window[win].v_cols );

	 	d_string.dsc$w_length = strlen( wtitle );	/* length of name */
	 	d_string.dsc$a_pointer = wtitle;			/* address of string */

		status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
/*		if (!(status & SS$_NORMAL)) lib$signal(status);	*/

		cursor( window[win].c_x, window[win].c_y );

		smg$read_keystroke( &keyboard, &key );

		switch (key)
		{
			case SMG$K_TRM_UPPERCASE_I    : if (window[win].v_rows>8)
							{
							window[win].p_rows -= 8;
							window[win].v_rows -= 8;
							}
							break;
			case SMG$K_TRM_UPPERCASE_J    : if (window[win].v_cols>8)
							{
							window[win].p_cols -= 8;
							window[win].v_cols -= 8;
							}
							break;
			case SMG$K_TRM_UPPERCASE_K    : window[win].p_cols += 8;
							window[win].v_cols += 8;
							break;
			case SMG$K_TRM_UPPERCASE_M    : window[win].p_rows += 8;
							window[win].v_rows += 8;
							break;
			case SMG$K_TRM_LOWERCASE_I    : if (window[win].v_rows>8)
							{
							window[win].p_rows -= 8;
							window[win].v_rows -= 8;
							}
							break;
			case SMG$K_TRM_LOWERCASE_J    : if (window[win].v_cols>8)
							{
							window[win].p_cols -= 8;
							window[win].v_cols -= 8;
							}
							break;
			case SMG$K_TRM_LOWERCASE_K    : window[win].p_cols += 8;
							window[win].v_cols += 8;
							break;
			case SMG$K_TRM_LOWERCASE_M    : window[win].p_rows += 8;
							window[win].v_rows += 8;
							break;
			case SMG$K_TRM_UP	      : if (window[win].v_rows>1)
							{
							window[win].p_rows -= 1;
							window[win].v_rows -= 1;
							}
							break;
			case SMG$K_TRM_DOWN  	      : window[win].p_rows += 1;
							window[win].v_rows += 1;
							break;
			case SMG$K_TRM_RIGHT 	      : window[win].p_cols += 1;
							window[win].v_cols += 1;
							break;
			case SMG$K_TRM_LEFT  	      : if (window[win].v_cols>1)
							{
							window[win].p_cols -= 1;
							window[win].v_cols -= 1;
							}
							break;
			case SMG$K_TRM_UPPERCASE_Q    : done = TRUE;
						        break;
			case SMG$K_TRM_LOWERCASE_Q    : done = TRUE;
						        break;
			case SMG$K_TRM_CR : done = TRUE;
					    break;
		}
		window[win].scroll_bot = window[win].v_rows;

/* and adjustment is made to both the virtual display and the viewport	*/

	        status = smg$change_virtual_display( &window[win].disp_id, &window[win].p_rows, &window[win].p_cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	        status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety, &window[win].v_offsetx,
						 &window[win].v_rows, &window[win].v_cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* Show the new size */

		status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
							&control_id );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}
	adjust_term_characteristics( win, window[win].v_rows, window[win].v_cols );

	if (( window[win].c_x > window[win].v_cols ) || (window[win].c_y > window[win].v_rows))
	{
		window[win].c_x = 1;
		window[win].c_y = 1;
		cursor( window[win].c_x, window[win].c_y );
	}

	sprintf( wtitle, "SWiM Term #%d", win+1 );

	d_string.dsc$w_length = strlen( wtitle );	/* length of name */
	d_string.dsc$a_pointer = wtitle;		/* address of string */

	status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
/*	if (!(status & SS$_NORMAL)) lib$signal(status);	*/

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





int move_cursor( int win, int *x, int *y )
{
 int done = FALSE, key = 0, xx, yy;


	sys$setast(0);
	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
						&control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	xx = *x;  yy = *y;
	smg$set_cursor_abs( &window[win].disp_id, &yy, &xx );
	while (!done)
	{
		smg$read_keystroke( &keyboard, &key );
		switch (key)
		{
			case SMG$K_TRM_CTRLE          : *x = window[win].v_cols;
				      break;
			case SMG$K_TRM_CTRLH          : *x = 1;
				      break;
			case SMG$K_TRM_UPPERCASE_I    : *y -= 8;
						if ( *y<window[win].v_offsety ) window[win].v_offsety -= 8;
				      break;
			case SMG$K_TRM_UPPERCASE_J    : *x -= 8;
				      break;
			case SMG$K_TRM_UPPERCASE_K    : *x += 8;
				      break;
			case SMG$K_TRM_UPPERCASE_M    : *y += 8;
						if ( *y>(window[win].v_offsety+window[win].v_rows) ) window[win].v_offsety += 8;
				      break;
			case SMG$K_TRM_LOWERCASE_I    : *y -= 8;
						if ( *y<window[win].v_offsety ) window[win].v_offsety -= 8;
				      break;
			case SMG$K_TRM_LOWERCASE_J    : *x -= 8;
				      break;
			case SMG$K_TRM_LOWERCASE_K    : *x += 8;
				      break;
			case SMG$K_TRM_LOWERCASE_M    : *y += 8;
						if ( *y>(window[win].v_offsety+window[win].v_rows) ) window[win].v_offsety += 8;
				      break;
			case SMG$K_TRM_UP    : *y -= 1;
						if ( *y<window[win].v_offsety ) window[win].v_offsety-- ;
				      break;
			case SMG$K_TRM_DOWN  : *y += 1;
						if ( *y>(window[win].v_offsety+window[win].v_rows) ) window[win].v_offsety++;
				      break;
			case SMG$K_TRM_PREV_SCREEN    : *y -= window[win].v_rows - 1;
						if ( *y<window[win].v_offsety ) window[win].v_offsety -= window[win].v_rows - 1;
				      break;
			case SMG$K_TRM_NEXT_SCREEN    : *y += window[win].v_rows - 1;
			if ( *y>(window[win].v_offsety+window[win].v_rows) ) window[win].v_offsety += window[win].v_rows - 1;
				      break;
			case SMG$K_TRM_RIGHT : *x += 1;
				      break;
			case SMG$K_TRM_LEFT  : *x -= 1;
				      break;
			case SMG$K_TRM_UPPERCASE_Q    : done = TRUE;
							return( 1 );
							break;
			case SMG$K_TRM_LOWERCASE_Q    : done = TRUE;
							return( 1 );
							break;
			case SMG$K_TRM_CR : done = TRUE;
					    break;
		}
		if ( *y<1) *y = 1;
		if ( *y>window[win].p_rows ) *y = window[win].p_rows;

		if ( *x<1) *x = 1;
		if ( *x>window[win].v_cols ) *x = window[win].v_cols;

		if ( window[win].v_offsety<1) window[win].v_offsety = 1;
		if ( window[win].v_offsety>(BACKSCROLLLINES+1) ) window[win].v_offsety = BACKSCROLLLINES+1;

		status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

		xx = *x;  yy = *y;
		status = smg$set_cursor_abs( &window[win].disp_id, &yy, &xx );
	}
	sys$setast(1);
	return 0;
}





/* Cut (actually just a copy) some data from a window	*/

void cut( int win )
{
 struct dsc$descriptor_d d_string;
 int done = FALSE, save_offsety;
 short int key;
 int start_x, start_y, end_x, end_y, count, i, j, k, real_row;
 char line[255],
      temp[5],
      save_char;


	save_offsety = window[win].v_offsety;

	d_string.dsc$w_length = 255;			/* length of line */
	d_string.dsc$a_pointer = line;			/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Move cursor to start position using Arrow Keys or I,J,K,M and press RETURN. Q)uit : ", 1, 1, FALSE );

	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
						&control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	current_display = window[win].disp_id;

	start_x = window[win].c_x;
	start_y = window[win].c_y + BACKSCROLLLINES;

	if (!move_cursor( win, &start_x, &start_y ))
	{
		real_row = start_y;
		status = smg$read_from_display( &current_display, &d_string, 0, &real_row );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

		rendition = SMG$M_REVERSE;
		save_char = line[start_x-1];
		temp[0] = save_char;
		temp[1] = '\0';
		puts_smg_w( temp, start_x, start_y - BACKSCROLLLINES, FALSE, FALSE, FALSE, FALSE );

		end_x = start_x;
		end_y = start_y;

		current_display = control_id;
		rendition = 0;

		status = smg$erase_display( &control_id );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

		puts_smg( "Move cursor to end position using Arrow Keys or I,J,K,M and press RETURN. Q)uit : ", 1, 1, FALSE );

		current_display = window[win].disp_id;

		if (!move_cursor( win, &end_x, &end_y ))
		{
			if ( start_y > end_y )		/* start should equal upper left corner	*/
			{
				i = start_y;
                                start_y = end_y;
                                end_y = i;
			}
			if ( start_x > end_x )
			{
				i = start_x;
                                start_x = end_x;
                                end_x = i;
			}
			count = start_y-1;
                        j = 0;
			while ( ++count < end_y+1 )
			{
				real_row = count;
				status = smg$read_from_display( &current_display, &d_string, 0, &real_row );
#if SAFE
				if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* get the data, strip high bits, remove control chars */

                                for( k=0, i=(start_x-1); i<(end_x-1); i++, k++ )
				{
					line[i] = line[i] & (char) 0x7f;
                                	if ( line[i]<'\x20' )
                                         cut_buffer[j][k] = '\x20';
                                	else cut_buffer[j][k] = line[i];
                                }
				cut_buffer[j][k]='\xd';
				cut_buffer[j][k+1]='\0';
                                j++;
			}
                        cut_width = end_x - start_x + 1;
                        cut_height = end_y - start_y + 1;
		}
		else
		{
			cut_width = -1;
			cut_height = -1;
		}
	}
	else
	{
		cut_width = -1;
		cut_height = -1;
	}
	rendition = 0;
	puts_smg_w( temp, start_x, start_y - BACKSCROLLLINES, FALSE, FALSE, FALSE, FALSE );

	window[win].v_offsety = save_offsety;

	status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* Paste the cut buffer to a window - this is equivalent to actually typing the data in the paste window */


void paste( int pwin )
{
 int i, j, width;

	if ( cut_width == -1 ) return;
	if ( cut_height == -1 ) return;


	for ( i=0; i<cut_height; i++ )
	{
		status = sys$clref( paste_efn );
		if (status == SS$_ILLEFC) lib$signal(status);

		paste_win = pwin;

/* strip the spaces off the right side of the string - just a little more efficient */

		j=0;
		while ( cut_buffer[i][j] != '\xd' ) j++;

		j--;
		while ( (cut_buffer[i][j] == ' ') && (j>0) ) j--;
		width = j+2;
		cut_buffer[i][width-1] = '\xd';

/* send the data to the PT and wait for completion */

#if FTA
		memcpy(ftadev[pwin].wbuff->data,&cut_buffer[i],Min(width,508));
		status = ptd$write(w_chan[pwin],0,0,ftadev[pwin].wbuff,width,0,0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		sys$clrast();
		memcpy(&ww_iosb[pwin],ftadev[pwin].wbuff,4);
#else
		status = sys$qiow ( 0, w_chan[pwin], IO$_WRITEVBLK, &ww_iosb[pwin], 0, 0,
					cut_buffer[i], width, 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
		if (ww_iosb[pwin].byte_cnt != width)   /* data overrun has occured...rewrite last bit of data for this line */
		{
			status = sys$setimr (wait_efn, &bintime, 0, 0);		/* wait .1 seconds for good measure */
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			status = sys$waitfr(wait_efn);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

			status = sys$clref( paste_efn );			/* reset paste event flag */
			if (status == SS$_ILLEFC) lib$signal(status);

			j = ww_iosb[pwin].byte_cnt;

#if FTA
		memcpy(ftadev[pwin].wbuff->data,(&cut_buffer[i])+j,Min(width-j,508));
		status = ptd$write(w_chan[pwin],0,0,ftadev[pwin].wbuff,width,0,0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		sys$clrast();
		memcpy(&ww_iosb[pwin],ftadev[pwin].wbuff,4);
#else
			status = sys$qiow ( 0, w_chan[pwin], IO$_WRITEVBLK, &ww_iosb[pwin], 0, 0,
						cut_buffer[i]+j, width-j, 0, 0, 0, 0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
		}

/* also wait for paste event flag to be set - data has actually been put in the window */

		status = sys$waitfr(paste_efn);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

/* wait .1 seconds as well just for good measure */

		status = sys$setimr (wait_efn, &bintime, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		status = sys$waitfr(wait_efn);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}

	control_key = '\0';			/* clear the control key which might be DATAOVERUN */

}





/* Freeze a window - send a control-s to the pt.  easy. */


void freeze_window( int win )
{
 struct dsc$descriptor_d d_string;
 vax$u_longword	rendition = SMG$M_REVERSE;
 char title[80];

	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	if ( window[win].frozen )
	{
		window[win].frozen = FALSE;
#if FTA
		memcpy(ftadev[win].wbuff->data,&ctrl_q[0],1);
		status = ptd$write(w_chan[win],0,0,ftadev[win].wbuff,1,0,0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
		status = sys$qiow( 0, w_chan[win], IO$_WRITEVBLK, 0, 0, 0, &ctrl_q[0], 1, 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif

		sprintf( title, "SWiM Term #%d", win+1 );

	 	d_string.dsc$w_length = strlen( title );	/* length of name */
	 	d_string.dsc$a_pointer = title;			/* address of string */

		sys$setast(0);
		status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
		sys$setast(1);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
        }
	else
	{
		window[win].frozen = TRUE;
#if FTA
		memcpy(ftadev[win].wbuff->data,&ctrl_s[0],1);
		status = ptd$write(w_chan[win],0,0,ftadev[win].wbuff,1,0,0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#else
		status = sys$qiow( 0, w_chan[win], IO$_WRITEVBLK, 0, 0, 0, &ctrl_s[0], 1, 0, 0, 0, 0);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
		sprintf( title, "SWiM Term #%d - FROZEN", win+1 );

	 	d_string.dsc$w_length = strlen( title );	/* length of name */
	 	d_string.dsc$a_pointer = title;			/* address of string */

		sys$setast(0);
		status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 );
		sys$setast(1);
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}
}





/* Maximize window to the full width and height of the screen and place it in the upper left
 * so it looks spiffy
 */

void max_window( int win )
{
 long int rows, cols, x, y, real_rows;
 char page;
 short int width;

	sys$setast(0);
	if ( window[win].maxed )
	{
		window[win].maxed = FALSE;
		rows = window[win].scroll_bot = window[win].save_rows;
		cols = window[win].p_cols = window[win].save_cols;

		window[win].p_rows = rows + BACKSCROLLLINES;
		real_rows = window[win].p_rows;

		window[win].v_rows = rows;
		window[win].v_cols = cols;

		window[win].p_x = window[win].save_x;
		window[win].p_y = window[win].save_y;

		status = smg$change_virtual_display( &window[win].disp_id, &real_rows, &cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	        status = smg$change_viewport( &window[win].disp_id, 0, 0, &window[win].v_rows, &window[win].v_cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

		page = rows;
		width = cols;
		adjust_term_characteristics( win, page, width );

		if (( window[win].c_x > window[win].v_cols ) || (window[win].c_y > window[win].v_rows))
		{
			window[win].c_x = 1;
			window[win].c_y = 1;
			cursor( window[win].c_x, window[win].c_y );
		}
	}
	else
	{
		window[win].maxed = TRUE;
		window[win].save_x = window[win].p_x;
		window[win].save_y = window[win].p_y;
		window[win].save_rows = window[win].v_rows;
		window[win].save_cols = window[win].v_cols;
		window[win].p_y = window[win].p_x = y = x = 1;

		rows = window[win].scroll_bot = pasteboard_rows;
		window[win].p_rows = rows + BACKSCROLLLINES;
		real_rows = window[win].p_rows;
		cols = window[win].p_cols = pasteboard_cols;

		window[win].v_rows = rows;
		window[win].v_cols = cols;

		status = smg$change_virtual_display( &window[win].disp_id, &real_rows, &cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	        status = smg$change_viewport( &window[win].disp_id, 0, 0, &window[win].v_rows, &window[win].v_cols );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

		page = rows;
		width = cols;
		adjust_term_characteristics( win, page, width );
	}
	sys$setast(1);
}





/* Backscroll a window, lets you take a quick look at what you missed.  Wouldn't you love to have
 * this on REAL terminals?  SWiM terminals aren't real?!?!
 */

void backscroll_display( int win )
{
 int done = FALSE, save_offsety;
 short int key;


	save_offsety = window[win].v_offsety;

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Backup using Arrow Keys or I,M.  Q)uit : ", 1, 1, FALSE );

	status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x,
						&control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	current_display = window[win].disp_id;

	while (!done)
	{
		cursor( window[win].c_x, window[win].c_y );
		smg$read_keystroke( &keyboard, &key );
		switch (key)
		{
			case SMG$K_TRM_UPPERCASE_I    : window[win].v_offsety -= 8;
				      break;
			case SMG$K_TRM_UPPERCASE_M    : window[win].v_offsety += 8;
				      break;
			case SMG$K_TRM_LOWERCASE_I    : window[win].v_offsety -= 8;
				      break;
			case SMG$K_TRM_LOWERCASE_M    : window[win].v_offsety += 8;
				      break;
			case SMG$K_TRM_PREV_SCREEN    : window[win].v_offsety -= window[win].v_rows - 1;
				      break;
			case SMG$K_TRM_NEXT_SCREEN    : window[win].v_offsety += window[win].v_rows - 1;
				      break;
			case SMG$K_TRM_UP : window[win].v_offsety -= 1;
				      break;
			case SMG$K_TRM_DOWN : window[win].v_offsety += 1;
				      break;
			case SMG$K_TRM_UPPERCASE_Q    : done = TRUE;
				      break;
			case SMG$K_TRM_LOWERCASE_Q    : done = TRUE;
				      break;
			case SMG$K_TRM_CR : done = TRUE;
					    break;
		}
		if ( window[win].v_offsety<1) window[win].v_offsety = 1;
		if ( window[win].v_offsety>(BACKSCROLLLINES+1) ) window[win].v_offsety = BACKSCROLLLINES+1;
		status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	}
	window[win].v_offsety = save_offsety;
	status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





/* This procedure will let you set or disable hot keys used by SWiM.
 * These keys then will be saved in the script file.
 */

void set_keys()
{
 short int key, key_number, done = FALSE;


	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Set Menu, Forward or Backward key or Quit ? ", 1, 1, FALSE );

	smg$read_keystroke( &keyboard, &key );
	switch ( key )
	{
	case	SMG$K_TRM_UPPERCASE_M	: key_number = 0;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_LOWERCASE_M	: key_number = 0;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_UPPERCASE_F	: key_number = 1;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_LOWERCASE_F	: key_number = 1;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_UPPERCASE_B	: key_number = 2;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_LOWERCASE_B	: key_number = 2;
                                          done = FALSE;
					  break;
	case	SMG$K_TRM_UPPERCASE_Q	: done = TRUE;
					  break;
	case	SMG$K_TRM_LOWERCASE_Q	: done = TRUE;
					  break;
		default			: done = TRUE;
					  break;
	}
	if ( !done )
	{
                smg$erase_display( &control_id );
		switch ( key_number )
		{
		case	0 : puts_smg( "New Menu key or Quit -> ", 1, 1, FALSE );
			    break;
		case	1 : puts_smg( "New Forward key or Quit or Disable -> ", 1, 1, FALSE );
			    break;
		case	2 : puts_smg( "New Backward key or Quit or Disable -> ", 1, 1, FALSE );
			    break;
		}
		smg$read_keystroke( &keyboard, &key );
		if ((key == SMG$K_TRM_UPPERCASE_Q ) || ( key == SMG$K_TRM_LOWERCASE_Q)) done = TRUE;
		if ( !done )
		{
			switch ( key_number )
			{
			case 0	: if (( key >= SMG$K_TRM_CTRLA ) && (key <= SMG$K_TRM_US ))
				   hot_key = key;
				  else
                                  {
                                  	smg$erase_display( &control_id );
                                        puts_smg( "Invalid key - key must be a control key.   Press a key.", 1, 1, FALSE );
					smg$read_keystroke( &keyboard, &key );
                                  }
				  break;
			case 1	: if (( key == SMG$K_TRM_UPPERCASE_D ) || (key == SMG$K_TRM_LOWERCASE_D ))
                        	   forward_key = '\0';
                                  else if (( key >= SMG$K_TRM_CTRLA ) && (key <= SMG$K_TRM_US ))
				   forward_key = key;
				  else
                                  {
                                  	smg$erase_display( &control_id );
                                        puts_smg( "Invalid key - key must be a control key.   Press a key.", 1, 1, FALSE );
					smg$read_keystroke( &keyboard, &key );
                                  }
				  break;
			case 2	: if (( key == SMG$K_TRM_UPPERCASE_D ) || (key == SMG$K_TRM_LOWERCASE_D ))
                        	   backward_key = '\0';
                                  else if (( key >= SMG$K_TRM_CTRLA ) && (key <= SMG$K_TRM_US ))
				   backward_key = key;
				  else
                                  {
                                  	smg$erase_display( &control_id );
                                        puts_smg( "Invalid key - key must be a control key.   Press a key.", 1, 1, FALSE );
					smg$read_keystroke( &keyboard, &key );
                                  }
				  break;
			}
		}
	}
        smg$erase_display( &control_id );
	sys$setast(1);
}





void help_window()
{
 struct dsc$descriptor_d d_string, topic_d;
 char help_lib[] = "swim_location:swim.hlb",
      topic[] = "SWIM";


	sys$setast(0);
	status = smg$erase_display( &help_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = smg$paste_virtual_display( &help_id, &pasteboard_id, &help_y, &help_x );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	d_string.dsc$w_length = strlen( help_lib );	/* length of name */
	d_string.dsc$a_pointer = help_lib;		/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	topic_d.dsc$w_length = strlen( topic );		/* length of name */
	topic_d.dsc$a_pointer = topic;			/* address of string */
	topic_d.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	topic_d.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = smg$put_help_text( &help_id, &keyboard, &topic_d, &d_string, 0, 0 );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = smg$unpaste_virtual_display( &help_id, &pasteboard_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





void task_window()
{
 vax$u_longword flags, x, y, choice;
 int i, win;
 struct dsc$descriptor_a menu;


	memset( &menu, 0, sizeof(menu) );

	menu.dsc$w_length  = 20;
	menu.dsc$b_dtype   = DSC$K_DTYPE_T;
	menu.dsc$b_class   = DSC$K_CLASS_A;
	menu.dsc$a_pointer = task_list_name;
	menu.dsc$b_scale   = 0;
	menu.dsc$b_digits  = 0;
	menu.dsc$b_dimct   = 1;
	menu.dsc$l_arsize  = sizeof(task_list_name);

	sys$setast(0);
	status = smg$erase_display( &task_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	x = 5;
	y = 3;
	flags = SMG$K_VERTICAL;
	status = smg$create_menu( &task_id, &menu, &flags, 0,0,0,0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	status = smg$paste_virtual_display( &task_id, &pasteboard_id, &y, &x );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	choice = 0;

	status = smg$select_from_menu( &keyboard, &task_id, &choice, 0, 0, 0, 0, 0, 0, 0, 0 );

	status = smg$unpaste_virtual_display( &task_id, &pasteboard_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	if ( choice != total_tasks )
	{
	     	i=0;					/* find an open window table	*/
		while( window[i].active ) i++;
		win = i;

		if ( (lwin+1)<MAXWIN )
		{
			choice--;
			task_ids[win] = task_list_id[choice];
			create_window( TRUE, task_list_x[choice], task_list_y[choice], task_list_height[i],
					     task_list_width[choice] );
			autologout[win] = TRUE;
		}
	}
	sys$setast(1);
}





print_a_line( struct dsc$descriptor *a_line, int user_arg )
{
 char temp[256];

	if ( (++p_line) > max_p_line )
	{
		printf( "\7" );
		control_key = PRINTDONE;
		sys$setef( control_efn );
	}
	strncpy( temp, a_line->dsc$a_pointer, a_line->dsc$w_length );
	temp[a_line->dsc$w_length]='\0';
	fputs( temp, cut_paste );
	printf("  line = %d    ", p_line );
}





void print_screen()
{
 struct dsc$descriptor_d d_string;
 char tempfile[41],
      queue[41];


	d_string.dsc$w_length = 41;			/* length of line */
	d_string.dsc$a_pointer = queue;			/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	sys$setast(0);
	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	puts_smg( "Name of queue to print screen to (SYS$PRINT) ? ", 1, 1, FALSE );

	read_string_smg( tempfile, 40 );

	if ( strlen( tempfile ) != 0 )
	 strcpy( queue, tempfile );
	else strcpy( queue, "SYS$PRINT" );

	d_string.dsc$w_length = strlen( queue );

	status = smg$erase_display( &control_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	
	smg$unpaste_virtual_display( &control_id, &pasteboard_id );

	smg$print_pasteboard( &pasteboard_id, &d_string );
	sys$setast(1);
}




void title_window()
{
 struct dsc$descriptor_d d_string;
 char menu_key[] = "Menu Key is CTRL- ";
 short int key;

	sys$setast(0);
	status = smg$erase_display( &title_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = smg$paste_virtual_display( &title_id, &pasteboard_id, &title_y, &title_x );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	d_string.dsc$w_length = strlen( titletime_s );	/* length of name */
	d_string.dsc$a_pointer = titletime_s;		/* address of string */
	d_string.dsc$b_class = DSC$K_CLASS_S;		/* string descriptor class */
	d_string.dsc$b_dtype = DSC$K_DTYPE_T;		/* data type: ASCII string */

	status = sys$bintim( &d_string, &titletime );		/* convert the paste title time to binary 	*/
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

	current_display = title_id;

	puts_smg("Welcome to", 10, 1, FALSE );

     	rendition = SMG$M_BOLD;
     	puts_smg("lqqqq x   x x lqwqk",  5, 3, TRUE );
	puts_smg("x     x   x   x x x",  5, 4, TRUE );
	puts_smg("mqqqk x x x x x x x",  5, 5, TRUE );
	puts_smg("    x x x x x x   x",  5, 6, TRUE );
	puts_smg("qqqqj mqvqj x x   x",  5, 7, TRUE );
	rendition = 0;

	puts_smg("version 3.1",  9, 9, FALSE );
	puts_smg("by Steve Jennen",  7, 10, FALSE );

	menu_key[strlen(menu_key)-1] = hot_key + 'A' - 1;

	rendition = SMG$M_BOLD;
        puts_smg( menu_key, 6, 12, FALSE );
	rendition = 0;
	sys$setast(1);

/*
	status = sys$setimr (wait_efn, &titletime, 0, 0);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	status = sys$waitfr(wait_efn);
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
*/

	setup_keyboard();
	sys$setast(0);
	puts_smg( "Press a key.", 9, 14, FALSE );
	smg$read_keystroke( &keyboard, &key );
	sys$setast(1);
	remove_keyboard();

	sys$setast(0);
	status = smg$unpaste_virtual_display( &title_id, &pasteboard_id );
#if SAFE
	if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
	sys$setast(1);
}





void do_fix()
{
 int i;

 i=0;
 while (i<MAXWIN)
 {
 	if (window[i].active) adjust_term_characteristics( i, window[i].v_rows, window[i].v_cols );
	i++;
 }
}





void load_script(char *filename)
{
 int fstatus, x, y, width, height, bool, i;
 char version[80];
 char fname[80];
 char temp[133];

 		i = 0;

		strcpy( fname, filename );

		if ( strchr( fname, '.' ) == 0 ) strcat( fname, ".swm" );

		if (( script = fopen( fname, "r" )) == NULL )
		{
			printf( "\nSWiM: Error opening script file.\n\n" );
			exit(0);
		}
		fgets( version, 79, script );
		if (strncmp( version, "SWiM", 4) == 0)
		{
			do
                        {
				fgets( temp, 132, script );
                        }
			while (temp[0] == '!');
			sscanf( temp, "%d %d %d", &hot_key, &forward_key, &backward_key );
                        while ( (fstatus = fgets( temp, 132, script )) != NULL)
                        {
				if (temp[0] != '!')
                                {
					sscanf( temp, "%d %d %d %d %d %d %c", &bool, &x, &y, 
										&height, &width,
										&autologout[i], &task_ids[i] );
					setup_keyboard();
					create_window( bool, x-1, y-1, height, width );
					remove_keyboard();
					i++;
                                }
                        }
		}
		else	/* load old style script file */
		{
                	fclose( script );
                        script = fopen( fname, "r");
			fscanf( script, "%d %d %d", &hot_key, &forward_key, &backward_key );
			while ( (fstatus = fscanf( script, "%d %d %d %d %d %d", &bool, &x, &y, 
									   &height, &width,
									   &autologout[i] )) != EOF)
			{
                        	task_ids[i] = '-';
				setup_keyboard();
				create_window( bool, x-1, y-1, height, width );
				remove_keyboard();
				i++;
			}
		}
		fclose( script );
}




/* This is... */


main( int argc, char *argv[] )
{
 int row = 1, col = 1, fstatus, x, y, width, height, bool, i, len, offset;
 short int key;
 char fname[81];

	for(i=0;i<MAXTASKS;i++) strcpy( task_list_name[i], "" );

	i = 0;
	if (( tasks = fopen( "task_list.swim", "r" )) != NULL )
	{
		while (( fstatus = fscanf( tasks, "%s %d %d %d %d %c", task_list_name[i], &task_list_x[i],
								       &task_list_y[i], &task_list_height[i],
								       &task_list_width[i], &task_list_id[i] )) != EOF )
		{
			i++;
		}
		fclose( tasks );
	}
      	strcpy( task_list_name[i], "Quit" );
	task_list_id[i] = '-';
	total_tasks = i+1;

	init_everything();

	if ( argc == 2 )	/* user wants to load scrip file */
	{
        	load_script( argv[1] );
	}
	else if ( argc>2 )	/* user doesn't know what is going on */
	{
		printf("\nusage:  SWiM [script_file]\n\n");
		exit(0);
	}
	else			/* setup the default window */
	{
		setup_keyboard();
		create_window(  TRUE,  1,  1, 24,  80 );
		remove_keyboard();
	}

	cursor( window[win].c_x, window[win].c_y );

	kbd_r_buff[0]='\0';
	kbd_iosb.byte_cnt = 0;

	title_window();

	control_key = '\0';

	while (1)	/* forever and ever...well, almost	*/
	{

/* Start read on the keyboard */

		sys$clref( control_efn );

		if ( control_key != DATAOVERUN )
		{
			status = sys$qio ( 0, kbd_chan, IO$_READVBLK, &kbd_iosb, kbd_r_ast, 0, &kbd_r_buff[0], 1,
					     0, &term_block, 0, 0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		}


/* This IS the program, right here, this line! */

		sys$waitfr( control_efn );

/* That IS the program, right above, that line! */


		sys$clref( control_efn );

		switch (control_key)
		{
		case DATAOVERUN : 	status = sys$waitfr(xon_efn);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif

					status = sys$clref( xon_efn );
					if (status == SS$_ILLEFC) lib$signal(status);

#if FTA
					memcpy(&ww_iosb[win],ftadev[win].wbuff,4);
#endif
					len = ww_len[win]-ww_iosb[win].byte_cnt;
					ww_len[win] = len;

#if FTA
					memcpy(ftadev[win].wbuff->data,xoff_buff,len);
					status = ptd$write(w_chan[win],0,0,ftadev[win].wbuff,len,0,0);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					memcpy(&ww_iosb[win],ftadev[win].wbuff,4);
#else
					status = sys$qiow ( 0, w_chan[win], IO$_WRITEVBLK, &ww_iosb[win], 0, 0,
								 xoff_buff, len, 0, 0, 0, 0);
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
#endif
					window[win].frozen = FALSE;
					break;
		case PRINTDONE : 	fclose( cut_paste );
					break;

		case CONTROL : 
			current_display = control_id;
        	
			sys$setast(0);
			status = smg$paste_virtual_display( &control_id, &pasteboard_id, &row, &col, 0 );
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			sys$setast(1);

			setup_keyboard();

			key = 0;

			sys$setast(0);
			smg$read_keystroke( &keyboard, &key );
			sys$setast(1);

			switch (key)
			{
			case SMG$K_TRM_ONE   : if ( window[0].active ) win = 0;
					       break;
			case SMG$K_TRM_TWO   : if ( window[1].active ) win = 1;
					       break;
                        case SMG$K_TRM_THREE : if ( window[2].active ) win = 2;
					       break;
                        case SMG$K_TRM_FOUR  : if ( window[3].active ) win = 3;
					       break;
                        case SMG$K_TRM_FIVE  : if ( window[4].active ) win = 4;
					       break;
                        case SMG$K_TRM_SIX   : if ( window[5].active ) win = 5;
					       break;
                        case SMG$K_TRM_SEVEN : if ( window[6].active ) win = 6;
					       break;
                        case SMG$K_TRM_EIGHT : if ( window[7].active ) win = 7;
					       break;
                        case SMG$K_TRM_NINE  : if ( window[8].active ) win = 8;
					       break;
                        case SMG$K_TRM_ZERO  : if ( window[9].active ) win = 9;
					       break;
			case SMG$K_TRM_UPPERCASE_A : adjust_size( win );
						     break;
			case SMG$K_TRM_LOWERCASE_A : adjust_size( win );
						     break;
			case SMG$K_TRM_UPPERCASE_B : backscroll_display( win );
						     break;
			case SMG$K_TRM_LOWERCASE_B : backscroll_display( win );
						     break;
			case SMG$K_TRM_UPPERCASE_C : cut( win );
						     break;
			case SMG$K_TRM_LOWERCASE_C : cut( win );
						     break;
#if DEBUG
			case SMG$K_TRM_UPPERCASE_D : monitor = !monitor;
						     data_on();
						     break;
			case SMG$K_TRM_LOWERCASE_D : monitor = !monitor;
						     data_on();
						     break;
#endif
			case SMG$K_TRM_UPPERCASE_F : freeze_window( win );
						     break;
			case SMG$K_TRM_LOWERCASE_F : freeze_window( win );
						     break;
			case SMG$K_TRM_LOWERCASE_H : help_window();
						     break;
			case SMG$K_TRM_UPPERCASE_H : help_window();
						     break;
			case SMG$K_TRM_LOWERCASE_I : sys$setast(0);
						     smg$print_pasteboard( &pasteboard_id );
						     sys$setast(1);
						     break;
			case SMG$K_TRM_UPPERCASE_I : print_screen();
						     break;
			case SMG$K_TRM_UPPERCASE_K : set_keys();
						     break;
			case SMG$K_TRM_LOWERCASE_K : set_keys();
						     break;
			case SMG$K_TRM_UPPERCASE_L : log_session( win );
						     break;
			case SMG$K_TRM_LOWERCASE_L : log_session( win );
						     break;
			case SMG$K_TRM_UPPERCASE_M : move_display( win );
						     break;
			case SMG$K_TRM_LOWERCASE_M : move_display( win );
						     break;
			case SMG$K_TRM_UPPERCASE_N : create_new_term();
						     break;
			case SMG$K_TRM_LOWERCASE_N : create_new_term();
						     break;
#if DEBUG
			case SMG$K_TRM_UPPERCASE_O : qmonitor = !qmonitor;
					sys$setast(0);
					status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 );
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					sys$setast(1);
						     break;
			case SMG$K_TRM_LOWERCASE_O : qmonitor = !qmonitor;
					sys$setast(0);
					status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 );
#if SAFE
					if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
					sys$setast(1);
						     break;
#endif
			case SMG$K_TRM_UPPERCASE_P : paste( win );
						     break;
			case SMG$K_TRM_LOWERCASE_P : paste( win );
						     break;
			case SMG$K_TRM_UPPERCASE_Q : sys$setast(0);
						     status = smg$erase_pasteboard( &pasteboard_id );
#if SAFE
						     if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
						     sys$setast(1);
						     exit(0);
						     break;
			case SMG$K_TRM_LOWERCASE_Q : sys$setast(0);
						     status = smg$erase_pasteboard( &pasteboard_id );
#if SAFE
						     if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
						     sys$setast(1);
						     exit(0);
						     break;
			case SMG$K_TRM_UPPERCASE_R : sys$setast(0);
						     status = smg$repaint_screen( &pasteboard_id );
#if SAFE
						     if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
						     sys$setast(1);
						     break;
			case SMG$K_TRM_LOWERCASE_R : sys$setast(0);
						     status = smg$repaint_screen( &pasteboard_id );
#if SAFE
						     if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
						     sys$setast(1);
						     break;
			case SMG$K_TRM_UPPERCASE_S : win++;
						     while( (!window[win].active) && (win<MAXWIN)) win++;
						     if ( win==MAXWIN )
						     {
                                                     	win = 0;
						     	while( (!window[win].active) && (win<MAXWIN)) win++;
                                                     }
						     break;
			case SMG$K_TRM_LOWERCASE_S : win++;
						     while( (!window[win].active) && (win<MAXWIN)) win++;
						     if ( win==MAXWIN )
						     {
                                                     	win = 0;
						     	while( (!window[win].active) && (win<MAXWIN)) win++;
                                                     }
						     break;
			case SMG$K_TRM_UPPERCASE_T : task_window();
						     break;
			case SMG$K_TRM_LOWERCASE_T : task_window();
						     break;
			case SMG$K_TRM_UPPERCASE_W : write_script();
						     break;
			case SMG$K_TRM_LOWERCASE_W : write_script();
						     break;
			case SMG$K_TRM_UPPERCASE_X : max_window( win );
						     break;
			case SMG$K_TRM_LOWERCASE_X : max_window( win );
						     break;
			case SMG$K_TRM_UPPERCASE_Z : do_fix();
						     break;
			case SMG$K_TRM_LOWERCASE_Z : do_fix();
						     break;
			}
			remove_keyboard();

			sys$setast(0);
	 		smg$unpaste_virtual_display( &control_id, &pasteboard_id );
			sys$setast(1);
			title();
			break;
		case FORWARD : 	while (!( window[++win].active ) && ( win<MAXWIN ));
				if ( win == MAXWIN )
				{
					win = -1;
					while (!( window[++win].active ) && ( win<MAXWIN ));
				}
				break;
		case BACKWARD :	while (!( window[--win].active ) && ( win>-1 ));
				if ( win == -1 )
				{
					win = MAXWIN;
					while (!( window[--win].active ) && ( win>-1 ));
				}
				break;
        	}
#if DEBUG
		if ( (!monitor) && (!qmonitor) )
		{
#endif
		sys$setast(0);
		status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x );
#if SAFE
		if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
		sys$setast(1);
#if DEBUG
		}
#endif

/* When switching to a new window, make sure user's terminal's keypad state is right for the session */

		if (( window[win].keypad ) && (!keypad_is_on))
		{
			status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadon[0], 2, 0, 0, 0, 0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			keypad_is_on = TRUE;
		}
		if ((!window[win].keypad) && (keypad_is_on))
		{
	        	status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadoff[0], 2, 0, 0, 0, 0);
#if SAFE
			if (!(status & SS$_NORMAL)) lib$signal(status);
#endif
			keypad_is_on = FALSE;
		}
		current_display = window[win].disp_id;

		cursor( window[win].c_x, window[win].c_y );
	}
}
