-h- aaareadme.1st SWiM version 1.0 by Steve Jennen jennen%tigger.decnet@msus1.bitnet Student Programmer, St. Cloud State University, St. Cloud, MN 56301 ========================================================================= Welcome to SWiM, the multi-session windowing terminal application for VAX/VMS. SWiM let's you open windows on your terminal screen, each window becoming a terminal in itself. In the windows then, you can log into your same account (the one running SWiM) or into a different account. Each terminal is an almost full featured VT100 terminal, running most popular applications available for the VAX. SWiM allows dynamic sizing of the windows, movement of windows to anywhere on or off the screen, CUT and PASTE between windows and a back-scroll feature to look at information that has just scrolled off your screen. Once you have the number of windows you're comfortable with and you've placed them in your favorite location, SWiM allows you to save that information in a script file. Next time you run SWiM, you can specify the loading of the script file and your setup will be properly reproduced. SWiM is fun, useful and addicting and I hope you enjoy it. To set up SWiM on your system, first install the pseudo-terminal drivers according to the documentation for them,i.e. the same ones used by DecWindows(TWDRIVER.EXE and PYDRIVER.EXE). $ RUN SYS$SYSTEM:SYSGEN LOAD TWDRIVER.EXE LOAD PYDRIVER.EXE CONNECT TWA0 /NOADAPTER /DRIVER=TWDRIVER CONNECT PYA0 /NOADAPTER /DRIVER=PYDRIVER Then, compile and link SWiM as follows: $ cc/noopt swim $ link/notrace swim,swim.opt/opt Or use the command procedure: $ @build Then, create the help library for SWiM: $ library/create/help swim swim SWiM uses a logical SWIM_LOCATION to point to the location of the help library. This logical must be defined before running SWiM. Setup SWIM as a foreign command: SWIM == "$swim_location:swim" It'll accept one parameter, that being the script-file-name: $ swim [script-file-spec] The script-file has a default extension of ".SWM", which will be added automatically by SWiM during the creation of the script file. [librarian's note: I have found that the SWIM program needs to be installed or run with privileges or it crashes as currently built. I install it with /priv=(share,phy_io) to allow it to reset characteristics of the pseudo terminals it uses. It is, once installed, a VERY nice package. I recompiled and linked from source at my machine when the supplied .exe failed. - Glenn Everhart ] -h- build.com $ cc/noopt swim $ link/notrace swim,swim.opt/opt $ lib/create=(blocks:1)/help swim swim -h- swim.c /* SWiM version 1.0 written July-August, 1990 * * SWiM - "Steve's Window Manager" * * by Steve Jennen, student (freshman!) programmer * * Academic Computer Services * St. Cloud State University, St. Cloud, MN * * jennen%tigger.decnet@msus1.bitnet * * SWiM Copyright 1990 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 * * * * * File: SWiM.C * * * This is the main program. * * */ #include #include #include #include #include #include #include /* include files for VAX SMG$ interface */ #include #include #include #include #include #include #include "types.h" /* SWiM's types */ $DESCRIPTOR( kbd_devnam, "tt:" ); /* user's terminal */ $DESCRIPTOR( wdevnam, "PYA0:" ); /* pseudo terminal control device */ /* 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 = "a; item_list.lenadr = "a_len; item_list.zero1 = 0; item_list.zero2 = 0; status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); return(quota); } /* 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 = "a; item_list.lenadr = "a_len; item_list.zero1 = 0; item_list.zero2 = 0; status = sys$getjpi( 0, 0, 0, &item_list, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); 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; smg$set_cursor_abs( ¤t_display, &real_y, &x ); } /* 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( ¤t_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( ¤t_display, &puts_string, &real_y, &x, &rendition, 0, &char_set ); else { if ( wide ) { x *= 2; smg$put_chars_wide( ¤t_display, &puts_string, &real_y, &x, &rendition, 0, &char_set ); } else if ( highwide ) { x *= 2; smg$put_chars_highwide( ¤t_display, &puts_string, &real_y, &x, &rendition, 0, &char_set ); } else smg$put_chars( ¤t_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( ¤t_display, &disp_height ); puts_smg( string, x, y, graphic ); if (y == disp_height) smg$scroll_display_area( ¤t_display ); } #if DEBUG void data_on() { status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); } #endif /* Setup error virtual display. */ void setup_error() { status = smg$erase_display( &error_id ); if (!(status & SS$_NORMAL)) lib$signal(status); status = smg$paste_virtual_display( &error_id, &pasteboard_id, &error_y, &error_x ); if (!(status & SS$_NORMAL)) lib$signal(status); } /* Remove error virtual display. */ void remove_error() { status = smg$unpaste_virtual_display( &error_id, &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); } /* 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; #if DEBUG status = smg$set_term_characteristics( &pasteboard_id, 0, 0, &noecho, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); #else status = smg$set_term_characteristics( &pasteboard_id, 0, 0, &noecho, &past, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); #endif status = smg$create_virtual_keyboard( &keyboard ); if (!(status & SS$_NORMAL)) lib$signal(status); } /* change the term characteristics back and delete the virtual keyboard */ void remove_keyboard() { unsigned int noecho = TT$M_NOECHO, past = TT2$M_PASTHRU; status = smg$delete_virtual_keyboard( &keyboard ); if (!(status & SS$_NORMAL)) lib$signal(status); #if DEBUG status = smg$set_term_characteristics( &pasteboard_id, &noecho, 0, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); #else status = smg$set_term_characteristics( &pasteboard_id, &noecho, &past, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); #endif } /* 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, ¤t_display ); if (!(status & SS$_NORMAL)) lib$signal(status); 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; current_display = control_id; rendition = 0; puts_smg( " dj ack ut rz elp eys og ov ew aste pr nt frsh witch 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", 23, 1, FALSE ); puts_smg( "L", 28, 1, FALSE ); puts_smg( "M", 32, 1, FALSE ); puts_smg( "N", 36, 1, FALSE ); puts_smg( "P", 40, 1, FALSE ); puts_smg( "I", 48, 1, FALSE ); puts_smg( "R", 52, 1, FALSE ); puts_smg( "S", 58, 1, FALSE ); puts_smg( "W", 65, 1, FALSE ); puts_smg( "X", 73, 1, FALSE ); puts_smg( "Q", 75, 1, FALSE ); smg$set_cursor_abs( ¤t_display, &y, &x ); rendition = 0; } /* Let's set everything up! */ void init_everything() { struct dsc$descriptor_d d_string; 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"; /* 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 (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); status = sys$dclexh (&exit_block_descrip); /* set up the exit handler */ if (!(status & SS$_NORMAL)) lib$signal(status); status = lib$get_ef( &control_efn ); /* get the CONTROL event flag */ if (!(status & SS$_NORMAL)) lib$signal(status); status = lib$get_ef( &wait_efn ); /* get the timer event flag */ if (!(status & SS$_NORMAL)) lib$signal(status); status = lib$get_ef( &paste_efn ); /* get the paste event flag */ if (!(status & SS$_NORMAL)) lib$signal(status); status = lib$get_ef( &xon_efn ); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$clref( xon_efn ); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$clref( control_efn ); /* clear control event flag */ if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$clref( paste_efn ); /* clear paste event flag */ if (!(status & SS$_NORMAL)) 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 (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); /* Setup the pasteboard and determine users's terminal height and width */ status = smg$create_pasteboard( &pasteboard_id, 0, &pasteboard_rows, &pasteboard_cols, 0, &pasteboard_termtype ); if ( status != SS$_NORMAL ) lib$signal( status ); if ( pasteboard_termtype != SMG$K_VTTERMTABLE ) { printf("\n\nYou must use a VT compatible terminal with this program.\n\n"); exit(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 ); title(); /* 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 (!(status & SS$_NORMAL)) lib$signal(status); #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 /* Initialize the window structs, etc. */ for( i=0; i 131 ) { last_io = fputc( '\xa', win_log[win] ); lf = 0; } } } /* 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 (!(status & SS$_NORMAL)) lib$signal(status); } /* 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$getdvi( 0, w_chan[win], 0, &dvi_stuff, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); sprintf( twname, "TWA%d:", dev_depend ); 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 (!(status & SS$_NORMAL)) lib$signal(status); #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; window_type twin; /* temporary window struct - used to avoid using 'window[rwin]' throughout the code */ sys$setast( 0 ); /* Disable ASTs...needed for SMG$ calls */ 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 (!(wr_iosb[rwin].status & SS$_NORMAL)) /* Check status of read from pt */ { if ( !twin.active ) /* PT has been deleted */ { sys$setast( 1 ); 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; puts_nl_smg( "Start of data.", 1, mony, FALSE ); up_mony(); monx = 1; for( i=0; i '\x1f')) /* Quick. printable character? */ { prefix[j++] = ch; } 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' : status = sys$qiow( 0, w_chan[rwin], IO$_WRITEVBLK, 0, 0, 0, &ansiterm[0], 7, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); escmode[rwin] = FALSE; break; /* Cursor down */ case 'D' : bot = twin.scroll_bot; if ( twin.c_y == bot ) { smg$scroll_display_area( ¤t_display ); } 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 ) { smg$scroll_display_area( ¤t_display ); } 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) ) { direction = SMG$M_DOWN; smg$scroll_display_area( ¤t_display, 0,0,0,0, &direction ); if ( twin.c_y' : twin.keypad = FALSE; status = sys$qiow( 0, kbd_chan, IO$_WRITEVBLK, 0, 0, 0, &keypadoff[0], 2, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); 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='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='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; smg$erase_chars( ¤t_display, &count, &real_y, &x ); } else if (twin.params[0]==2) /* the whole line */ { scol = 1; real_y = twin.c_y + BACKSCROLLLINES; smg$erase_line( ¤t_display, &real_y, &scol ); } else /* from cursor to end of line */ { real_y = twin.c_y + BACKSCROLLLINES; count = twin.v_cols - twin.c_x + 1; smg$erase_chars( ¤t_display, &count, &real_y, &twin.c_x ); } 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; smg$erase_display( ¤t_display, &srow, &scol, &real_y, &twin.c_x ); smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x ); } else if (twin.params[0] == 2 ) /* Entire screen */ { srow = BACKSCROLLLINES+1; scol = 1; real_y = twin.p_rows; smg$erase_display( ¤t_display, &srow, &scol, &real_y, &twin.p_cols ); smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x ); } else /* from cursor to end of screen */ { srow = twin.p_rows; scol = twin.p_cols; real_y = twin.c_y + BACKSCROLLLINES; smg$erase_display( ¤t_display, &real_y, &twin.c_x, &srow, &scol ); smg$move_virtual_display( &twin.disp_id, &pasteboard_id, &twin.p_y, &twin.p_x ); } 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; smg$delete_line( ¤t_display, &real_y, &count ); 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; k0 ) /* put what we have in the prefix string to the window */ { 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; j=0; } switch (ch) { /* Line feed */ case '\xa' : bot = twin.scroll_bot; if ( twin.c_y == bot ) { smg$scroll_display_area( ¤t_display ); } else twin.c_y++; break; case '\xb' : bot = twin.scroll_bot; if ( twin.c_y == bot ) { smg$scroll_display_area( ¤t_display ); } else twin.c_y++; break; case '\xc' : bot = twin.scroll_bot; if ( twin.c_y == bot ) { smg$scroll_display_area( ¤t_display ); } 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' : puts_smg_w( "\x7", twin.c_x, twin.c_y, twin.g1, twin.insert, twin.wide, twin.highwide ); 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 */ { 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; } window[rwin] = twin; /* update the real window struct */ current_display = window[win].disp_id; cursor( window[win].c_x, window[win].c_y ); #if DEBUG if ( monitor ) { current_display = data_id; puts_nl_smg( "", 1, mony, FALSE ); 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 ); puts_nl_smg( temp, 1, mony, FALSE ); up_mony(); monx = 0; } #endif /* Reset read on PT device */ 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); } sys$setast( 1 ); /* turn ASTs back on */ if ( paste_win == rwin ) /* Ok to continue paste operation - must be timed precisely */ { status = sys$setef(paste_efn); if (!(status & SS$_NORMAL)) 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 (!(status & SS$_NORMAL)) lib$signal(status); */ } /* 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; vax$u_longword bordered = SMG$M_BORDER, rendition = SMG$M_REVERSE; int i, io_quota, key, priority; char twname[15], mbxname[15], title[80], command[80], quota[80]; sys$setast( 0 ); 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; 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 ); remove_error(); sys$setast( 1 ); return; } if ( get_ast_quota() < 5 ) /* Must have enough quota! */ { setup_error(); current_display = error_id; 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 ); remove_error(); sys$setast( 1 ); 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; puts_nl_smg( quota, qx, qy, FALSE ); } #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 */ 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 (!(status & SS$_NORMAL)) lib$signal(status); /* Create a termination mailbox for the PT */ status = sys$crembx( 0, &mbx_chan[win], 0, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$getdvi( 0, mbx_chan[win], 0, &mbx_stuff, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 */ /* 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 (!(status & SS$_NORMAL)) lib$signal(status); /* 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 (!(status & SS$_NORMAL)) lib$signal(status); status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, xoff_ast, win, 0, 2, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$qiow( 0, w_chan[win], IO$_SETMODE, &wx_iosb[win], 0, 0, set_mode_ast, win, 0, 3, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); /* 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 (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); current_display = window[win].disp_id; if (same_user) { puts_smg_w( "Creating interactive process, please wait...", window[win].c_x, window[win].c_y, FALSE, FALSE, FALSE, FALSE ); window[win].c_y += 2; status = sys$getdvi( 0, w_chan[win], 0, &dvi_stuff, 0, 0, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); sprintf( twname, "TWA%d:", dev_depend ); 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 */ priority = get_priority(); /* use controlling process's base priority for new session */ status = sys$creprc( 0, &image, &d_twname, &d_twname, 0,0,0,0, priority,0,0, stsflg ); /* MAgiC! */ if (!(status & SS$_NORMAL)) lib$signal(status); } else { puts_smg_w( "Press RETURN to login.", window[win].c_x, window[win].c_y, FALSE, FALSE, FALSE, FALSE ); 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; puts_nl_smg( quota, qx, qy, FALSE ); } #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; } sys$setast( 1 ); } 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; sys$setast( 0 ); /* 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 (!(status & SS$_NORMAL)) lib$signal(status); 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; 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 (!(status & SS$_NORMAL)) lib$signal(status); } else puts_smg_w( "\x7", window[win].c_x, window[win].c_y, window[win].g1, window[win].insert, window[win].wide, window[win].highwide ); 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 (!(status & SS$_NORMAL)) lib$signal(status); } sys$setast( 1 ); } /* Menu option to setup new SWiM session */ void create_new_term() { short int key; int same = FALSE, logout = FALSE, doit = TRUE; status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); puts_smg( "Log in under same username (Y/n/Quit) ? ", 1, 1, FALSE ); smg$read_keystroke( &keyboard, &key ); 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 ) { puts_smg( "Automatically delete window when process logs out (y/N/Quit) ? ", 1, 1, FALSE ); smg$read_keystroke( &keyboard, &key ); 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 ( lwin8) { 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 (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); /* 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 (!(status & SS$_NORMAL)) lib$signal(status); } 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 (!(status & SS$_NORMAL)) lib$signal(status); } int move_cursor( int win, int *x, int *y ) { int done = FALSE, key = 0; cursor( *x, *y ); while (!done) { smg$read_keystroke( &keyboard, &key ); switch (key) { case SMG$K_TRM_UPPERCASE_I : *y -= 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; break; case SMG$K_TRM_LOWERCASE_I : *y -= 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; break; case SMG$K_TRM_UP : *y -= 1; break; case SMG$K_TRM_DOWN : *y += 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].v_rows ) *y = window[win].v_rows; if ( *x<1) *x = 1; if ( *x>window[win].v_cols ) *x = window[win].v_cols; cursor( *x, *y ); } 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; 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; 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 */ status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); current_display = window[win].disp_id; start_x = window[win].c_x; start_y = window[win].c_y; if (!move_cursor( win, &start_x, &start_y )) { real_row = start_y + BACKSCROLLLINES; status = smg$read_from_display( ¤t_display, &d_string, 0, &real_row ); if (!(status & SS$_NORMAL)) lib$signal(status); 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, 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 (!(status & SS$_NORMAL)) lib$signal(status); 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 + BACKSCROLLLINES; status = smg$read_from_display( ¤t_display, &d_string, 0, &real_row ); if (!(status & SS$_NORMAL)) lib$signal(status); /* 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, FALSE, FALSE, FALSE, FALSE ); status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); } /* Paste the cut buffer to a window - this is equivalent to actually typing the data in the paste window */ void paste( int win ) { int i, j, width; if ( cut_width == -1 ) return; if ( cut_height == -1 ) return; sys$setast( 1 ); for ( i=0; i0) ) j--; width = j+2; cut_buffer[i][width-1] = '\xd'; /* send the data to the PT and wait for completion */ status = sys$qiow ( 0, w_chan[win], IO$_WRITEVBLK, &ww_iosb[win], 0, 0, cut_buffer[i], width, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); /* also wait for paste event flag to be set - data has actually been put in the window */ status = sys$waitfr(paste_efn); if (!(status & SS$_NORMAL)) lib$signal(status); /* wait .1 seconds as well just for good measure */ status = sys$setimr (wait_efn, &bintime, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$waitfr(wait_efn); if (!(status & SS$_NORMAL)) lib$signal(status); } sys$setast( 0 ); } /* 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; status = sys$qiow( 0, w_chan[win], IO$_WRITEVBLK, 0, 0, 0, &ctrl_q[0], 1, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); 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 */ status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); } else { window[win].frozen = TRUE; status = sys$qiow( 0, w_chan[win], IO$_WRITEVBLK, 0, 0, 0, &ctrl_s[0], 1, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); 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 */ status = smg$label_border( &window[win].disp_id, &d_string, 0, 0, &rendition, 0, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); } } /* 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; 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 (!(status & SS$_NORMAL)) lib$signal(status); status = smg$change_viewport( &window[win].disp_id, 0, 0, &window[win].v_rows, &window[win].v_cols ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); status = smg$change_viewport( &window[win].disp_id, 0, 0, &window[win].v_rows, &window[win].v_cols ); if (!(status & SS$_NORMAL)) lib$signal(status); page = rows; width = cols; adjust_term_characteristics( win, page, width ); } } /* 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; status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); 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_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 (!(status & SS$_NORMAL)) lib$signal(status); } window[win].v_offsety = save_offsety; status = smg$change_viewport( &window[win].disp_id, &window[win].v_offsety ); if (!(status & SS$_NORMAL)) lib$signal(status); status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); } /* 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() { int key, key_number, done = FALSE; status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); 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; break; case SMG$K_TRM_LOWERCASE_M : key_number = 0; break; case SMG$K_TRM_UPPERCASE_F : key_number = 1; break; case SMG$K_TRM_LOWERCASE_F : key_number = 1; break; case SMG$K_TRM_UPPERCASE_B : key_number = 2; break; case SMG$K_TRM_LOWERCASE_B : key_number = 2; 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_CTRLZ )) 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_CTRLZ )) 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_CTRLZ )) 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 ); } void help_window() { struct dsc$descriptor_d d_string, topic_d; char help_lib[] = "swim_location:swim.hlb", topic[] = "SWIM"; status = smg$erase_display( &help_id ); if (!(status & SS$_NORMAL)) lib$signal(status); status = smg$paste_virtual_display( &help_id, &pasteboard_id, &help_y, &help_x ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); status = smg$unpaste_virtual_display( &help_id, &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); } 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 */ status = smg$erase_display( &control_id ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); smg$unpaste_virtual_display( &control_id, &pasteboard_id ); smg$print_pasteboard( &pasteboard_id, &d_string ); } void title_window() { struct dsc$descriptor_d d_string; char menu_key[] = "Menu Key is CTRL- "; int key; status = smg$erase_display( &title_id ); if (!(status & SS$_NORMAL)) lib$signal(status); status = smg$paste_virtual_display( &title_id, &pasteboard_id, &title_y, &title_x ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); current_display = title_id; sys$setast( 0 ); /* disable interrupts so our printing isn't interrupted */ 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 1.0", 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; /* status = sys$setimr (wait_efn, &titletime, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); status = sys$waitfr(wait_efn); if (!(status & SS$_NORMAL)) lib$signal(status); */ setup_keyboard(); puts_smg( "Press a key.", 9, 14, FALSE ); sys$setast( 1 ); smg$read_keystroke( &keyboard, &key ); remove_keyboard(); status = smg$unpaste_virtual_display( &title_id, &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); } void do_fix() { int i; i=0; while (i2 ) /* 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 (!(status & SS$_NORMAL)) lib$signal(status); } sys$setast( 1 ); /* This IS the program, right here, this line! */ sys$waitfr( control_efn ); /* That IS the program, right above, that line! */ sys$setast( 0 ); sys$clref( control_efn ); switch (control_key) { case DATAOVERUN : sys$setast(1); status = sys$waitfr(xon_efn); if (!(status & SS$_NORMAL)) lib$signal(status); sys$setast(0); status = sys$clref( xon_efn ); if (!(status & SS$_NORMAL)) lib$signal(status); len = ww_len[win]-ww_iosb[win].byte_cnt; ww_len[win] = len; status = sys$qiow ( 0, w_chan[win], IO$_WRITEVBLK, &ww_iosb[win], 0, 0, xoff_buff, len, 0, 0, 0, 0); if (!(status & SS$_NORMAL)) lib$signal(status); window[win].frozen = FALSE; break; case PRINTDONE : fclose( cut_paste ); break; case CONTROL : current_display = control_id; status = smg$paste_virtual_display( &control_id, &pasteboard_id, &row, &col, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); setup_keyboard(); key = 0; smg$read_keystroke( &keyboard, &key ); 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 : smg$print_pasteboard( &pasteboard_id ); 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; status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); break; case SMG$K_TRM_LOWERCASE_O : qmonitor = !qmonitor; status = smg$paste_virtual_display( &data_id, &pasteboard_id, &data_row, &data_col, 0 ); if (!(status & SS$_NORMAL)) lib$signal(status); 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 : status = smg$erase_pasteboard( &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); exit(0); break; case SMG$K_TRM_LOWERCASE_Q : status = smg$erase_pasteboard( &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); exit(0); break; case SMG$K_TRM_UPPERCASE_R : status = smg$repaint_screen( &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); break; case SMG$K_TRM_LOWERCASE_R : status = smg$repaint_screen( &pasteboard_id ); if (!(status & SS$_NORMAL)) lib$signal(status); break; case SMG$K_TRM_UPPERCASE_S : win++; while( (!window[win].active) && (win-1 )); if ( win == -1 ) { win = MAXWIN; while (!( window[--win].active ) && ( win>-1 )); } break; } #if DEBUG if ( (!monitor) && (!qmonitor) ) { #endif status = smg$repaste_virtual_display( &window[win].disp_id, &pasteboard_id, &window[win].p_y, &window[win].p_x ); if (!(status & SS$_NORMAL)) lib$signal(status); #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 (!(status & SS$_NORMAL)) lib$signal(status); 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 (!(status & SS$_NORMAL)) lib$signal(status); keypad_is_on = FALSE; } current_display = window[win].disp_id; cursor( window[win].c_x, window[win].c_y ); } } -h- swim.hlp 1 SWIM The command SWIM runs SWiM version 1.0, a multi-session windowing application for the VAX. SWiM allows the user to open up windows on their character-cell terminal screen, each window emulating a DEC VT100 terminal. SWiM allows dynamic sizing of each window, movement of windows anywhere on or off screen, cut and paste between windows and the ability to save a script file with the user's preferences. Format: SWIM [script-file-spec] 2 Parameter script-file-spec Specifies the name of a script file that contains the user's preferences as to number of windows and their positions. Also in the script file are any redefined hot-keys made before the script file was written. 2 Hot_keys There are three default hot-keys recognized by SWiM. The Menu_key is control-d, the forward_key is control-f and the backward_key is control-b. These keys will not be sent through to any of the windows. If it is necessary to send these keys through, the hot-keys may be redefined or disabled. 2 Menu_Commands ADJUST Allows sizing of the current active window. When adjust is first selected, the title on the active window changes, showing the current number of rows and number of columns. Adjustment is made using the arrow keys (to adjust by 1 char increment) or by the keys I,J,K and M (adjusting by 8 char increment). After the desired size is reached, press RETURN to accept it. If a script file is written after a window is sized, the size will be saved in the script file. NOTE: The terminal characteristics PAGE and WIDTH will be changed to reflect the new size. Most VAX applications and editors will read the current charactistics and use them when they are invoked. BACKUP Allows backing up in a terminal session to view information that has scrolled off the display. Move up and down with the arrow keys or the keys I and M. Press RETURN to return to the session. Currently, two full pages (50 lines) of information are saved in the backup buffer for each window. CUT Allows the selection of a block of text to be PASTEd into another window. Selection is made by two opposing corners of a block, such as the upper-left corner and the lower-right corner. Move the cursor to one corner, press RETURN, move the cursor to another corner and again press RETURN. The information will be saved in a buffer until a PASTE command is issued. FREEZE Disallows data to continue to flow into or out of a window until another FREEZE command is issued. This is analogous to a Hold-Screen function, operating on individual windows. KEYS Allows the redefining or disabling of the hot-keys. The Menu_Key may be redefined but NOT disabled. Each of the forward and backward keys may be redefined or disabled. Select which key to operate on and when asked, what key the selected key should become. Only CONTROL keys are allowed as hot-keys. CONTROL keys are keys made in conjuction with the 'Ctrl' key. Use the Ctrl key as the shift-key is used to modify a character. LOG Allows the logging of a session. This will save all the data going to a window (terminal) to a file in either of two formats: full or stripped. In full mode, all data is saved, including any control characters or escape sequences received at the terminal. In stripped mode, all control characters (except CR/LF) and escape sequences are removed before the information is written to the log file. A default log file name is provided, but may be changed. MOVE Allows the movement of a window to anywhere on or off the screen. Use the arrow keys or the keys I,J,K or M to move the window. If a script file is written after a window is moved, the window position will be saved in the script file. NEW Allows the creation of a new window/terminal session if the user has enough buffered_IO_byte_count quota. You may log into the same username again or into a different username. If you wish to use the same username, you must have enough max_jobs quota to support another session. An option is also given to automatically delete a window after the user logs out of the session in the window. PASTE This will enter into the current window any information from the cut buffer as if the information had actually been typed into the terminal session. The information will be pasted at the current cursor location. PRINT This will print a copy of the current screen image. If you activate this command with a capital I, then you will be allowed to specify a print queue to send the image to, otherwise it will be sent to SYS$PRINT. REFRESH This will repaint the entire display with the correct information. SWITCH This will switch to the next window session in the series. WRITE Allows the creation of a script file. Enter the name of the script file and press return. A default extension of .SWM will be added to the script file if an extension is not entered. Contained in the script file will be the current set of hot-keys, the number of windows and all the window positions. The next time SWiM is run specifying the newly created script file, all the windows and positions will be restored exactly. MAX Changes the current active window size and position to that of the physical terminal's size (from when SWiM was run). The window then will fill the terminal screen. If maX is selected again, the window size and position will be restored. QUIT Exit the SWiM program. All sessions currently active when Quit is selected will be disconnected from the VAX. 2 ADJUST Allows sizing of the current active window. When adjust is first selected, the title on the active window changes, showing the current number of rows and number of columns. Adjustment is made using the arrow keys (to adjust by 1 char increment) or by the keys I,J,K and M (adjusting by 8 char increment). After the desired size is reached, press RETURN to accept it. If a script file is written after a window is sized, the size will be saved in the script file. NOTE: The terminal characteristics PAGE and WIDTH will be changed to reflect the new size. Most VAX applications and editors will read the current charactistics and use them when they are invoked. 2 BACKUP Allows backing up in a terminal session to view information that has scrolled off the display. Move up and down with the arrow keys or the keys I and M. Press RETURN to return to the session. Currently, two full pages (50 lines) of information are saved in the backup buffer for each window. 2 CUT Allows the selection of a block of text to be PASTEd into another window. Selection is made by two opposing corners of a block, such as the upper-left corner and the lower-right corner. Move the cursor to one corner, press RETURN, move the cursor to another corner and again press RETURN. The information will be saved in a buffer until a PASTE command is issued. 2 FREEZE Disallows data to continue to flow into or out of a window until another FREEZE command is issued. This is analogous to a Hold-Screen function, operating on individual windows. 2 KEYS Allows the redefining or disabling of the hot-keys. The Menu_Key may be redefined but NOT disabled. Each of the forward and backward keys may be redefined or disabled. Select which key to operate on and when asked, what key the selected key should become. Only CONTROL keys are allowed as hot-keys. CONTROL keys are keys made in conjuction with the 'Ctrl' key. Use the Ctrl key as the shift-key is used to modify a character. 2 LOG Allows the logging of a session. This will save all the data going to a window (terminal) to a file in either of two formats: full or stripped. In full mode, all data is saved, including any control characters or escape sequences received at the terminal. In stripped mode, all control characters (except CR/LF) and escape sequences are removed before the information is written to the log file. A default log file name is provided, but may be changed. 2 MOVE Allows the movement of a window to anywhere on or off the screen. Use the arrow keys or the keys I,J,K or M to move the window. If a script file is written after a window is moved, the window position will be saved in the script file. 2 NEW Allows the creation of a new window/terminal session if the user has enough buffered_IO_byte_count quota. You may log into the same username again or into a different username. If you wish to use the same username, you must have enough max_jobs quota to support another session. An option is also given to automatically delete a window after the user logs out of the session in the window. 2 PASTE This will enter into the current window any information from the cut buffer as if the information had actually been typed into the terminal session. The information will be pasted at the current cursor location. 2 PRINT This will print a copy of the current screen image. If you activate this command with a capital I, then you will be allowed to specify a print queue to send the image to, otherwise it will be sent to SYS$PRINT. 2 REFRESH This will repaint the entire display with the correct information in the event that the information on the display is incorrent. This may occur after hitting the Break key on the terminal and the session is then resumed from the DECserver prompt. 2 SWITCH This will switch to the next window session in the series. 2 WRITE Allows the creation of a script file. Enter the name of the script file and press return. A default extension of .SWM will be added to the script file if an extension is not entered. Contained in the script file will be the current set of hot-keys, the number of windows and all the window positions. The next time SWiM is run specifying the newly created script file, all the windows and positions will be restored exactly. 2 MAX Changes the current active window size and position to that of the physical terminal's size (from when SWiM was run). The window then will fill the terminal screen. If maX is selected again, the window size and position will be restored. 2 QUIT Exit the SWiM program. All sessions currently active when Quit is selected will be disconnected from the VAX. -h- swim.opt sys$share:vaxcrtl.exe/share -h- swimprog.doc SWiM version 1.0 by Steve Jennen jennen%tigger.decnet@msus1.bitnet Student Programmer, St. Cloud State University, St. Cloud, MN 56301 ========================================================================= This document describes how SWiM works. The most important part of the SWiM program is the pseudo-terminal drivers. Without these drivers, SWiM would not exist. Pseudo-terminals (PTs) are a device for the VAX that act as a terminal, but only software can do input/output to them. Pseudo-terminals (PTs) come in device pairs, namely a PYAn: and a TWAn: device. The device number N is the same for each device. The PYAn: device is the control device and the TWAn: device is the actual pseudo-terminal. PTs are also self-cloning devices, when a channel is assigned to PYA0: a new PYAn: and TWAn: device pair are created. When the channel is deassigned, the devices are deleted. SWiM uses one pair for each window/terminal session active. Once a channel is open to a PT, a carriage return sent to the PT causes sys$system:loginout.exe to be executed. Alternatively, $CREPRC can be used to create a process that's attached to the TWAn: device which is what SWiM uses when it creates an interactive session under the same 'parent' username. It is a relatively simple concept to do input/output to the PT. Any data sent to the PT is the same as data being typed at a physical terminal. Any data received from the PT is data coming from the VAX the same as data printing out on your terminal screen. What SWiM does is this. Any data typed on the user's physical terminal is 'sent' through to the PT associated with the current active window. Any data received from the PT associated with the window is sent through a VT100 emulation routine. The routine interprets almost all VT100 compatible escape sequences and emulates their behavior in a virtual display window. For example, a CR received from the PT causes the cursor to move to the left margin on the current line in the virtual display. A sequence of "ESC[2J" causes the virtual display to be cleared, and so on. The really neat thing from a programming perspective is the fact that I didn't need to do anything to the data coming from the user's physical terminal except send it through to the PT. If the VAX decides to echo it back, it's considered data coming from the PT and runs through the VT100 emulation routine like all other data. Asychronous QIO calls are made throughout SWiM to give the illusion that all sessions are running simultaneously. In actuality, of course, only one window at a time is updated. A basic structural outline of SWiM follows: ================================================================================ Initialize variables Initialize SMG interface Assign channel to user's physical terminal if setup_script setup windows according to script assign channels w/ mailboxes for each pseudo-terminal desired else setup default window assign channel w/ mailbox to one pseudo-terminal endif start READs on all pseudo-terminal devices and pass PT_read_AST address LOOP start READ on user's terminal and pass keyboard_read_AST address wait for control_event /* depression of hot-key */ process HOT-KEY goto LOOP ------------------------------------------------------------------------------- Keyboard_read_AST look for HOT-KEY and set event flag if necessary send data directly to active pseudo-terminal device reset read on keyboard ------------------------------------------------------------------------------- Pseudo-terminal_read_AST send data through to virtual display interpreting any ANSI escape sequences reset read on PT ------------------------------------------------------------------------------- Pseudo-terminal_termination_mailbox_read_AST deassign pseudo-terminal device channel delete SMG windows ------------------------------------------------------------------------------- =============================================================================== That should sum up the basic operation and functioning of the program. There are a few other items that I need to discuss, however. To make the backup feature for SWiM, I simply made each virtual display 50 lines higher than they need to be. When data is received at the PT and needs to be sent to a virtual display, I simply add a 50 line offset to the y coordinate for the smg$ function calls. When running SWiM on a real VT100 terminal (and on some emulators) I've noticed that the screen management system is improperly sending an invalid escape sequence that will look like "',3H" and will appear on the top of the screen. This is NOT a bug in SWiM, it's just a quirk of the SMG facility. Also while writing SWiM, I discovered a bug in the VAXC interface with SMG$. When a virtual display is off the top of the screen, a call to smg$erase_display will not erase the display until another smg$ call is made to put_chars to another window or a repaste is done of the current window. To get around this, I called smg$move_display after each call to smg$erase_display in the VT100 emulation routine thereby insuring the display will be erased properly. This bug should be fixed in the next version of VAXC. I have implemented proper xon/xoff notification from each pseudo-terminal device. When a PT sends an xoff, I immediatly cease to pass any data through to the PT coming from the user's physical terminal. When this occurs, SWiM enters into a wait state until an xon occurs at which time any data that wasn't originally accepted by the PT during the write is re-sent. It is still possible that a keystroke may be missed (most notably at slower than 1200 baud rates), but this can be fixed by increasing the size of the buffer used to read from the keyboard called MAXKBDBUF in TYPES.H. I have the size currently set to 128 bytes and this has been adequate on our system for baud rates down to 1200. This may need to be increased for your system, the best way to tell is to see if EVE gets an invalid escape sequence while scrolling the display from depression of the arrow keys. -h- swimuser.doc +---- | | | +-+-+ | | | | | | +---+ | | | | | | | | | | | | | | ----+ +-+-+ | | | SWiM "Steve's Window Manager" by Steve Jennen Student Programmer, Academic Computer Services St. Cloud State University St. Cloud, MN 56301 jennen%tigger.decnet@msus1.bitnet DISCLAIMER SWiM is Copyright 1990 St. Cloud State University, St. Cloud, MN. This software is provided "as is" and St. Cloud State University provides no warranties of any kind. You may copy and distribute this program as long as this title page remains intact. If you find any bugs in the program or have any comments about the program, I'd like to hear about it. Send mail to: jennen%tigger.decnet@msus1.bitnet Contents I. What is SWiM? II. Getting Started III. The SWiM Reference Section IV. Hints for Optimal Performance V. Known bugs I. What is SWiM? SWiM stands for "Steve's Window Manager" which just about sums up what it does. SWiM manages windows on your character-cell terminal screen, each window being a VT100 terminal connected to the VAX. You can 'log-in' in a window, run your favorite VAX application in that window, open another window and log-in to a different account and check your mail, open another window and use the phone utility, and on and on. SWiM does not spawn processes for each window, it creates a totally separate interactive process as if you were using a physical terminal for each window on the screen. To give you a better idea of what I mean, take a look at this picture: +----------------------------------SWiM Term #4------------------------------- | |$ show term |Terminal: _TWA11: Device_Type: VT100 Owner: MACROsoft, Inc. | +--------------SWiM Term #1--------------+ | |$ if f$mode() .nes. "INTERACTIVE" then `| | Input: 9600 LFf|$ @pname | | Output: 9600 CRf|$ set term/line_edit | | |$ set term/insert | |Termin+------------------+$ set noverify |-----+ | Int| MAIL |$ set noon | | | Hos| # From |$ on error then goto WRAP_UP | | | Wra| | Buffer: LOGIN.COM | Insert | Forward | | | Bro| 1 WSU::SYSMGR | | | | No | 2 TIGGER::CS27|80 lines read from file STUDENT:[N00006`| | | No | 3 TIGGER::JENN+----------------------------------------+ | | Lin| 4 TIGGER::JENNEN 3-JUL-1990 disk prices...this is the| | No | 5 TIGGER::JENNEN 3-JUL-1990 are you ALWAYS on nobroad|d | No | 6 MKVAX1::MOZART 3-JUL-1990 RE: are you ALWAYS on nob|eypa | ANS| |o | No |Press RETURN for more... | |$ | | | |MAIL> | | +-----------------------------------------------------------------+ +----------------------------------------------------------------------------- In this picture, you see three windows doing three different things: DCL prompt, Mail and EVE. Again, in each window is a completely separate interactive processs, NOT subprocesses. In fact, the window labeled "SWiM Term #1" is actually logged in to an account that is different from the other two windows. When a window is opened, you may log into the same account (the account running SWiM) or to a different account. When you select a different account, the window appears blank until you press a carriage return in the window at which time you will be given your system's login screen and will be prompted for username and password. Notice also in this picture, that two of the windows are smaller than a typical 80x24 VT100 screen and that both EVE and MAIL are 'fitting' themselves to the abnormal sizes. SWiM allows you to dynamically size each window and will automatically alter the terminal's characteristics to reflect that new size. Most VAX applications read the current characteristics and will fit themselves accordingly. SWiM has many features that a normal VT100 terminal does not have, and has many features that work across windows. As has already been mentioned, SWiM allows dynamic sizing of each window. Windows may be moved anywhere on or off the physical terminal's screen. SWiM also supports the creation of a script-file that saves the sizes and positions of all the windows during a SWiM session. When you run SWiM the next time, you may specify the loading of a script-file with your favorite setup in it. SWiM also has features not found on a VT100 terminal or even more advanced terminals such as the VT200 series or VT300 series. SWiM has a feature called 'backup' that lets you scroll a window down to see information that has scrolled up off the top of the window. Many PC communication programs have a feature like that, but imagine it on your VAX terminal! Also supported is a Cut and Paste feature. You may mark a block of text in one window, cut it and then paste it into another window. When you paste data from one window into another, it's exactly the same as typing the data into the destination window. My favorite use of the cut and paste feature is during the development of an application on the VAX. I can run EVE in one window and the application in the other window. With cut and paste, I can easily copy screen images from the application and paste them directly into the documentation! Cut and Paste is also handy if you need to show an error message or a stack dump to someone else over the PHONE utility. You may be developing a program in one window and you get an error. You can phone a friend in another window, copy the error right off the screen and paste it into the phone conversation. That's quite a bit better than faxing stuff from within the phone utiltity. Another great built-in feature of SWiM is the ability to log a terminal session. It's similar to SET HOST/LOG or to the PHOTO program, but SWiM has two levels of logging that I call full and stripped. A full log session contains every byte coming from the VAX to the window, including all VT100 escape sequences and control characters. When you TYPE a full log file, you get an instant-replay view of the logged session. A stripped log session in contrast removes all escape sequences and control characters except for ff, lf, and cr. A stripped log session is the only log session suitable for printing, a printer will likely try to interpret the escape sequences of a full log session producing undesireable results. More than one window at a time may have logging turned on as well. SWiM has on-line help available at any time, in the format of the VMS help library. The help library may be installed on the system or remain available only from within SWiM. Some of the other features of the program are: freezing of a window, printing a copy of the screen to any print queue, refreshing the screen image and the ability to instantly maximize a window to the full screen size. SWiM, of course, has a Quit function and when you invoke it, all the processes in all the windows are stopped (it's as if you hung-up the phone on each window terminal). To accomplish all these wonderful tasks, SWiM takes control of your keyboard on your terminal and you send commands to SWiM through the use of hot-keys. There are three hot-keys: menu key, forward switch and backward switch. The menu-key brings up the menu on the top of the screen containing a list of all commands that are invoked through the use of a single letter. The forward switch and backward switch operate the same as on a DECserver, the forward switch moves you to the next session, in the case of SWiM, to the next window. The backward switch operates the same way in the other direction. The hot-keys, by default are defined as CTRL-D, CTRL-F, CTRL-B for the menu-key, forward switch and backward switch respectively. These keys are user-definable, the only limitation is they must be a control key. Additionally, the forward switch and backward switch may be disabled, freeing up the key so it may be passed through to the application being run. There are many applications for SWiM, and I myself am discovering new ideas every day. Now, let's get started in using SWiM! II. Getting Started To run the SWiM program, make sure your system administrator has properly installed the program as per the documentation. Then, simply type: $ SWiM at the DCL prompt. Of course, case-sensitivity does not apply, so typing "Swim", "SWiM", "swim" or "SWIM" are all the same things in DCL. Once run, SWiM will open up one window on your terminal screen and the title box will appear and then disappear after your first keystroke. The default window it creates is located such that the top line of the window is on the top line of your physical terminal's screen. If you have your terminal currently at 80 columns by 24 lines, one column and one row will be 'off' the screen because of the window's border. You should also notice that your login.com program was run when the process was created. You now have two processes running on the VAX, one process dedicated to running SWiM and controlling the window and one process in a window. On a SHOW USERS list, you should appear as being logged in twice, once on a real terminal or LTA terminal and one on a device named TWAn:. The one logged in on the TWAn: device is the process in the window. Before I show you more, I would like to mention two limiting factors concerning the creation of processes (the opening of windows). VAX/VMS has many process quotas limiting what a user can and cannot do. SWiM relies on a certain amount of allocated quota for your process, namely the quota called MAX ACTIVE JOBS and the one called BUFFERED I/O BYTE COUNT QUOTA. MAX ACTIVE JOBS limits the number of times you may be logged into the VAX at the same time. If your MAX ACTIVE JOBS quota is 5, you can log onto the VAX from 5 physical terminals at the same time. From withing SWiM, with the same amount of quota, you may have up to 4 windows running (under the same username) because remember, one process must control the SWiM session. The other quota, BUFFERED I/O BYTE COUNT QUOTA is also used to create a session, although it's not quite cut and dry as the other quota. SWiM uses approximately 1600 bytes of quota for each session created. If the quota goes below twice that amount, SWiM will not allow the creation of a new session to maintain system integrity. SWiM will also notify you that you've run out through a dialog box. You should talk to your system administrator if any of your quotas are insufficient to use SWiM the way you'd like to. Let's assume that you have plenty of quota to play around with SWiM. I'm also going to assume that you're at a terminal and the width and height of the terminal are 80x24 respectively. Now, let's fix the window by making it the full size of the screen. Do this by typing CTRL-D and then pressing the X key. Then, create another window by typing CTRL-D and then pressing the N key. You will be asked two additional questions, simply press the RETURN key for each of those. A new window will appear right on top of the other one. After your login procedure has completed, type CTRL-D and then X to make it full-size like the other one. Ok, go ahead and do something in this window, like go into the editor or mail or phone or type directory. Then, press the CTRL-F key and watch the screen. The screen will be repainted with the other active session still going in it. You can flip back again by pressing CTRL-F again, and so on. You can run different applications in each window and easily switch between them. Go ahead and create another window by following the same steps and see how much fun it is. Let's try moving and resizing a window. Type CTRL-D and then M to start moving a window. You are now in 'move mode' for the active window. By pressing the arrow keys, or the i,j,k,m keys you can move the window around on the screen. For purposes of this example, please move the window partway down the screen. Now, to change the size of the window, press RETURN to get out of move mode for the window. Then, press CTRL-D and then A to 'adjust' the window size. By pressing the arrow keys, or the i,j,k,m keys you change the size of the window. Notice that the title of the window has changed to show you the current page and width sizes. Make the window 60 wide and 12 high. Exit the adjust mode for the window by pressing RETURN. It's possible now that your cursor in the active window could be off the screen. If you had done anything in the window that caused the cursor to be below line 12, it will now be outsize of the window border and you will not see any more output until the cursor is returned to the physical window. I suggest that you clear your screen in the window at this time, I assume that you have some way of doing that from within DCL, i.e. a escape sequence or command that puts the cursor in the home position, e.g. WRITE SYS$OUTPUT "''ESC'[H''ESC'[2J" or SHOW CLUSTER works too. Once your window is resized, try running the EVE editor from within that window. EVE will look to see the size of the window and fit itself within it accordingly. The last thing I am going to show you at this point, is how to quit SWiM. It's a good idea to not be running any programs in any of the windows when you quit SWiM, especially a text-editor. Many programs use temporary data files when you run them, if you kill the process running the program, the temporary files will probably not be deleted. For this reason, stop running any programs in all the windows. Then, type CTRL-D and Q to quit. You will be returned to your original DCL prompt, and all the other processes are deleted. The next section of this document describes all the commands of SWiM in detail. III. The SWiM Reference Section SWiM is run from the DCL prompt with this syntax: $ SWiM [script-file-spec] SWiM allows only one parameter, that being the file-spec for a script file created by SWiM at some earlier time. See the WRITE command for information on creating a script file. ----------------------------------------------------------------------- Once in SWiM, there are three hot keys used to access different functions of SWiM: CTRL-D Access the SWiM menu from which other commands can be run. CTRL-F Goes Forward to the next window, if any. CTRL-B Goes Backward to the previous window, if any. The hot-keys may be redefined, please see the KEYS command for more information. ----------------------------------------------------------------------- From the Menu, the SWiM commands are: Adjust change the size of a window Backup look at data that may have scrolled off the window Cut copy a block of text to the copy buffer Freeze toggle freezing a window - dis/allow any input/output Help access the on-line help facility Keys change/disable hot-keys Log toggle logging of a window session Move move a window around New create a new window session Paste paste the data from the copy buffer to the window prInt send a copy of the terminal screen to a print queue Refresh redraw the entire screen Switch go forward to next session (same as CTRL-F) Write create a script file maX maximize a window - make it the same size as the screen Quit exit the SWiM program 1-9 switch immediatly to window number 1-9 ----------------------------------------------------------------------- The commands in more detail - ADJUST Allows sizing of the current active window. When adjust is first selected, the title on the active window changes, showing the current number of rows and number of columns. Adjustment is made using the arrow keys (to adjust by 1 char increment) or by the keys I,J,K and M (adjusting by 8 char increment). After the desired size is reached, press RETURN to accept it. If a script file is written after a window is sized, the size will be saved in the script file. NOTE: The terminal characteristics PAGE and WIDTH will be changed to reflect the new size. Most VAX applications and editors will read the current charactistics and use them when they are invoked. BACKUP Allows backing up in a terminal session to view information that has scrolled off the display. Move up and down with the arrow keys or the keys I and M. Press RETURN to return to the session. Currently, two full pages (50 lines) of information are saved in the backup buffer for each window. CUT Allows the selection of a block of text to be PASTEd into another window. Selection is made by two opposing corners of a block, such as the upper-left corner and the lower-right corner. Move the cursor to one corner, press RETURN, move the cursor to another corner and again press RETURN. The information will be saved in a buffer until a PASTE command is issued. FREEZE Disallows data to continue to flow into or out of a window until another FREEZE command is issued. This is analogous to a Hold-Screen function, operating on individual windows. KEYS Allows the redefining or disabling of the hot-keys. The Menu_Key may be redefined but NOT disabled. Each of the forward and backward keys may be redefined or disabled. Select which key to operate on and when asked, what key the selected key should become. Only CONTROL keys are allowed as hot-keys. CONTROL keys are keys made in conjuction with the 'Ctrl' key. Use the Ctrl key as the shift-key is used to modify a character. LOG Allows the logging of a session. This will save all the data going to a window (terminal) to a file in either of two formats: full or stripped. In full mode, all data is saved, including any control characters or escape sequences received at the terminal. In stripped mode, all control characters (except CR/LF) and escape sequences are removed before the information is written to the log file. A default log file name is provided, but may be changed. MOVE Allows the movement of a window to anywhere on or off the screen. Use the arrow keys or the keys I,J,K or M to move the window. If a script file is written after a window is moved, the window position will be saved in the script file. NEW Allows the creation of a new window/terminal session if the user has enough buffered_IO_byte_count quota. You may log into the same username again or into a different username. If you wish to use the same username, you must have enough max_jobs quota to support another session. An option is also given to automatically delete a window after the user logs out of the session in the window. PASTE This will enter into the current window any information from the cut buffer as if the information had actually been typed into the terminal session. The information will be pasted at the current cursor location. PRINT This will print a copy of the current screen image. If you activate this command with a capital I, then you will be allowed to specify a print queue to send the image to, otherwise it will be sent to SYS$PRINT. REFRESH This will repaint the entire display with the correct information. SWITCH This will switch to the next window session in the series. WRITE Allows the creation of a script file. Enter the name of the script file and press return. A default extension of .SWM will be added to the script file if an extension is not entered. Contained in the script file will be the current set of hot-keys, the number of windows and all the window positions. The next time SWiM is run specifying the newly created script file, all the windows and positions will be restored exactly. MAX Changes the current active window size and position to that of the physical terminal's size (from when SWiM was run). The window then will fill the terminal screen. If maX is selected again, the window size and position will be restored. QUIT Exit the SWiM program. All sessions currently active when Quit is selected will be disconnected from the VAX. IV. Hints for Optimal Performance After using SWiM for a duration, you may notice that the scrolling of a window can be rather slow and at other times, it's just as fast as a normal terminal. The key to all of this is the position of the windows on the screen. If you have a window that stretches from one side of the screen to the other with no overlapping windows, that window will scroll just as fast as a real terminal. When windows overlap, the screen management facility has much more work to do to scroll a window. The amount of data that is transfered can be quite a lot, as each scroll may require a full redraw of the window. On my own terminal, which is a Falco VT300 compatible, I have it set up to be 132 columns by 50 rows. The setup I like the most is to have two full size (132x24) windows on my screen at a time - one on the top and one on the bottom. Each window stretches the width of the screen, and therefore the scrolling in each window is fast. I sometimes also have a third window (80x24) that sits somewhere on top of the other two when it's active and behind them both when it's not. I mainly use that smaller window for applications that don't require a lot of scrolling, such as PHONE or MAIL or MONITOR. At home, on the other hand, when I'm dialing up the VAX from my PC, I don't have the luxury of a 132x50 line screen. The setup I use at home usually is to have each window the same size as the screen, 80x24. I can then simply switch between them with the forward or backward hot-keys, and each window will fill the entire display. The effect of this is that the screen is simply redrawn with the next window. I'm sure you'll find a setup that suits you well, and I hope you have fun with SWiM! V. Known Bugs Problem Description: Sometimes when logging is enabled using the STRIP mode, characters are randomly missed and not logged. It has not been determined yet what the exact conditions are that produce these results. Response: I've looked over SWiM's data logging routines, and they look fine. I'm not sure exactly why the routines miss some characters, but I have a theory. The ASTs are shut off during the calls to 'fputc', which SWiM uses to send the data coming from the terminal to the file. 'fputc' sits on top of RMS, and it may be that the write to the file does not complete as soon as my write to file routine completes at which time the ASTs are turned back on. I know that the SMG$ routines are not reentrant, that is, they can not be interrupted by an AST and complete their function. This may also be true of the RMS routines, and that's why the logging of SWiM missed characters. I _can_ fix this, but it would take more time than we have. Why don't we just leave it for the next release of SWiM? -steve -h- types.h /* SWiM version 1.0 written July-August, 1990 * * SWiM - "Steve's Window Manager" * * by Steve Jennen, student (freshman!) programmer * * SWiM Copyright 1990 St. Cloud State University, St. Cloud, MN * * Academic Computer Services * St. Cloud State University, St. Cloud, MN * * jennen%tigger.decnet@msus1.bitnet * * * * 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 * * * File: TYPES.H * * * All data structures and global variables are declared here. * */ /* If you are going to INSTALL this program with the SHARE priveledge, set * the following to 1. */ #define PRIVS 1 #define TRUE 1 #define FALSE 0 #define STRLEN 256 #define MAXWIN 9 /* maximum number of allowed windows */ #define WRBUFFLEN 256 /* the window-read max buffer length */ #define MAXKBDBUF 128 /* the maximum read from keyboard buffer length */ #define CUTBUFFLINES 50 #define CUTBUFFWIDTH 256 #define BACKSCROLLLINES 50 /* HOT-KEY numbers - only used internally by SWiM */ #define CONTROL 0 #define FORWARD 1 #define BACKWARD 2 #define DATAOVERUN 3 #define PRINTDONE 4 typedef char string[STRLEN]; /* some VAX internal data types made simple */ typedef unsigned short int vax$u_word; typedef long int vax$longword; typedef unsigned long int vax$u_longword; /* THEE window structure. This contains size, location and general states (such as graphics, keypad, video * attributes and so on) of each window which contains a pseudo-terminal session. */ typedef struct window_struct { int active, /* window exists */ same; /* same username */ vax$u_longword disp_id; /* display ID */ int frozen, /* frozen state */ maxed, /* window has been maxed to pasteboard size and fills the screen */ logging, /* Logging of a session is enabled. 1=full, 2=stripped */ c_x, /* cursor_x */ c_y, /* cursor_y */ omode, /* terminal is in cursor origin mode */ insert, /* terminal is in insert mode */ g1, /* graphics characters are selected */ save_c_x, /* save cursor x - vt100 ESC7 function */ save_c_y, /* save cursor y - vt100 ESC7 function */ highwide, /* in highwide mode */ bottom, wide; /* in wide char mode */ vax$longword p_x, /* physical x */ p_y, /* physical y */ p_rows, /* physical rows */ p_cols, /* physical columns */ save_x, /* save of physical x before MAX */ save_y, /* save of physical y before MAX */ save_rows, /* save of terminal rows before MAX */ save_cols, /* save of terminal cols before MAX */ /* view port is currently unimplemented */ v_rows, /* viewport rows */ v_cols, /* viewport columns */ v_offsetx, /* viewport offset x */ v_offsety, /* viewport offset y */ scroll_top, /* scroll region top */ scroll_bot; /* scroll region bottom */ vax$u_longword video_attributes, /* just what it says */ char_set, /* ditto */ save_video_attributes, /* for vt100 ESC7 */ save_char_set, /* for vt100 ESC7 */ keypad; /* term's keypad is in application mode */ int border; /* is display bordered */ string name; /* border name */ int params[10], /* up to 10 parameters for the current esc sequence */ p_num; /* parameter counter */ } window_type; /* Device characteristics block for a terminal device. */ struct devchar { char class; char type; short int buffer_size; unsigned int basic_char; unsigned int extended_char; }; /* I/O status block for a terminal device. */ struct iosb { short int status; /* io status word */ short int byte_cnt; /* number of bytes just read/written */ short int terminator; short int terminator_size; }; struct itmlst { short int buflen; short int item; unsigned int bufadr; unsigned int lenadr; int zero1; int zero2; }; /* struct used for GETDVI on a terminal */ typedef struct DVIBLK { unsigned short len, code; char * buffp; long * lenp; long terminate; } DVIBLK; int term_mask[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int exit_handler(); int exit_status; /* exit handler block descriptor for $DCLEXH */ struct exit_block { int flink; int *hanlder_addr; char arg_count; char unused[3]; int reason; } exit_block_descrip = {0, exit_handler, 1, 0, 0, 0, &exit_status}; struct term_descrip { short int size; short int unused; int *ptr; } term_block = {32, 0, &term_mask[0]}; struct dsc$descriptor_d puts_string; vax$u_longword status, /* general status word for system calls */ pasteboard_id, /* ... */ cursor_line_id, /* cursor line virtual displayu - current unimplemented */ pasteboard_rows, /* size of pasteboard in rows */ pasteboard_cols, /* size of pasteboard in cols */ pasteboard_termtype, /* termtype of pasteboard - must bt VTTERMTABLE */ current_display, /* current display id of active display */ control_id, /* display id of control menu display */ help_id, help_x, help_y, keyboard, /* keyboard id for SMG */ rendition, /* current rendition set */ data_id, error_id, title_id, title_x, title_y, error_x, error_y, io_quota_d, do_io_quota = TRUE; window_type window[MAXWIN]; /* ALL the windows */ int win, /* current active window */ lwin, /* total number of active windows */ monitor = FALSE, /* data monitor mode - used only for debugging purposes */ qmonitor = FALSE, monx = 0, /* ditto */ mony = 0, /* monitor cursor location */ qx = 0, /* ditto */ qy = 0, /* monitor cursor location */ data_row = 4, data_col = 50, data_cols = 80, data_rows = 20; lf = 0; short int control_efn, /* event flag to signify press of HOT-KEY */ control_key, /* hot key # */ wait_efn, paste_efn, paste_win, xon_efn; short int kbd_chan, /* channel of keyboard - user's physical terminal */ w_chan[MAXWIN], /* i/o channels for all pseudo-terminal devices by window */ mbx_chan[MAXWIN]; /* i/o channels for all pseudo-terminal termination mailboxes */ int ww_len[MAXWIN]; struct iosb kbd_iosb, /* keyboard i/o status block */ wr_iosb[MAXWIN], /* window read i/o status block */ ww_iosb[MAXWIN], /* window write iosb */ wx_iosb[MAXWIN], /* xon/xoff iosb */ mbx_iosb[MAXWIN], /* termination mailbox iosb */ twset_iosb; struct devchar changed_char; /* new terminal characteristics */ struct devchar saved_char; /* original terminal characteristics */ char wr_buff[MAXWIN] [WRBUFFLEN]; /* window read buffers */ char mbx_buff[MAXWIN] [40]; /* termination mailbox buffer */ char kbd_r_buff[MAXKBDBUF]; /* read buffer for i/o from user's physical terminal */ char xoff_buff[MAXKBDBUF]; /* read buffer for i/o from user's physical terminal */ char cut_buffer[CUTBUFFLINES][CUTBUFFWIDTH]; int cut_width, cut_height; char ansiterm[8] = "\x1b[?1;2c", /* hey! I'm a VT100 terminal! */ keypadon[] = "\x1b=", /* put terminal in keypad application mode */ keypadoff[] = "\x1b>", /* put terminal in keypad numeric mode */ ctrl_s[] = "\x13", /* hold screen */ ctrl_q[] = "\x11", /* unhold screen */ hot_key = '\x4', term_ok[] = "\x1b[0n", /* status ok */ curpos_start[] = "\x1b[", curpos_end[] = "R", no_printer[] = "\x1b[?13n", forward_key = '\x6', backward_key = '\x2'; int keypad_is_on = FALSE; /* state of physical terminal keypad mode */ int escmode[MAXWIN], /* ESCape mode interpretation sequence number */ autologout[MAXWIN], ignore = FALSE, bintime[2], titletime[2], second[2], p_line, max_p_line; char bintime_s[] = "0 ::.1"; char titletime_s[] = "0 ::5"; char second_s[] = "0 ::1"; $DESCRIPTOR( image, "SYS$SYSTEM:LOGINOUT.EXE" ); /* image to execute for same username login */ vax$u_longword stsflg = PRC$M_INTER + PRC$M_DETACH + PRC$M_NOPASSWORD; /* status flag for $CREPRC */ int dev_depend; /* device dependent info for GETDVI */ struct DVIBLK dvi_stuff = {4, DVI$_DEVDEPEND, &dev_depend, 0, 0}; struct DVIBLK mbx_stuff = {4, DVI$_UNIT, &dev_depend, 0, 0}; FILE *script, /* script file handle */ *cut_paste, /* cut and paste file handle */ *win_log[MAXWIN]; /* logging session file handle */