diff options
Diffstat (limited to 'tecdisp.c')
-rw-r--r-- | tecdisp.c | 2867 |
1 files changed, 2867 insertions, 0 deletions
diff --git a/tecdisp.c b/tecdisp.c new file mode 100644 index 0000000..a1d2bb6 --- /dev/null +++ b/tecdisp.c @@ -0,0 +1,2867 @@ +char *tecdisp_c_version = "tecdisp.c: $Revision: 1.1 $"; + +/* + * $Date: 2007/12/10 21:59:20 $ + * $Source: /cvsroot/videoteco/videoteco/tecdisp.c,v $ + * $Revision: 1.1 $ + * $Locker: $ + */ + +/* tecdisp.c + * Terminal Screen Display Subroutines + * + * COPYRIGHT (c) 1985-2003 BY + * PAUL CANTRELL & J. M. NISHINAGA + * SUDBURY, MA 01776 + * ALL RIGHTS RESERVED + * + * This software is furnished in it's current state free of charge. + * The authors reserve all rights to the software. Further + * distribution of the software is not authorized. Modifications to + * the software may be made locally, but shall not be distributed + * without the consent of the authors. This software or any other + * copies thereof, may not be provided or otherwise made available + * to anyone without express permission of the authors. Title to and + * ownership of this software remains with the authors. + * + */ + +#include "teco.h" +#include "tecparse.h" + +/* + * Global Storage is defined here for lack of a better place. + */ +/* + * External Variables + */ + extern char teco_startup; + extern char screen_startup; + extern int term_columns; + extern int term_lines; + extern int forced_height,forced_width; + extern int tty_input_chan; + extern int tty_output_chan; + extern char insert_delete_line_capability; + extern char eight_bit_output_flag; + extern char hide_cr_flag; + extern char tab_expand[]; + extern int tab_width; + +#ifdef TERMCAP + extern int termcap_sg; +#endif + +#ifdef TERMINFO + extern int terminfo_magic_cookie_glitch; +#endif + +/* + * Global Variables + */ + int curx; + int cury; + short cur_mode; + int cursor_x; + int cursor_y; + int screen_sequence; + int echo_cnt; + char ring_audible_bell; + int buffer_li; + int buffer_half_li; + char format_lines_invalid; + + struct format_line * + allocate_format_buffer(struct window *,struct buff_line *); + struct format_line * + screen_find_window_format_line(struct window *,struct buff_line *); + struct window *create_window(int,int); + void screen_optimize_lines(void); + void screen_optimize_by_pattern_matching(void); + void screen_gc_format_lines(void); + void screen_free_window_format_lines(struct window *,struct buff_line *); + void screen_account_for_delete_line(int y, int count); + void screen_account_for_insert_line(int y, int count); + int screen_rebuild_from_scratch(struct window *wptr); + int screen_format_buff_line(struct window *wptr,struct buff_line *lbp); + int screen_find_dot(struct window *wptr); + +/* + * Global structures + */ + struct screen_line *saved_screen; + char saved_screen_valid; + + int next_window_number = 1; + struct window *curwin; + struct window *window_list; + struct format_line message_line; + char message_flag = NO; + struct format_line echo_line; + + struct format_line *format_line_free_list = NULL; + struct format_line *format_line_alloc_list = NULL; + +/* + * External Routines, etc. + */ + extern struct buff_header *curbuf; + extern struct buff_header *buffer_headers; + + extern int term_speed; + extern char input_pending_flag; + extern char resize_flag; + +/* SCREEN_INIT - Initialize the TECO screen package + * + * Function: + * + * This entry point gives the screen package a chance to initialize + * itself. + */ +int +screen_init() +{ +register int i,j; +register struct window *wptr; +register struct screen_line *lp; +register short *sp; + + PREAMBLE(); + +/* + * Call the terminal handling routines, and give them a chance to set + * the terminal up. + */ + term_init(); + +/* + * Set the cursor to the upper left of the display and perform a clear to end + * of display command. This has the effect of clearing the entire display. + */ + term_goto(0,0); + term_clrtobot(); + +/* + * If this is a restart (from ^Z most likely), then that is all we have to do. + * The rest of this is only to initialize data structures the first time + * through + */ + if(screen_startup == NO) return(SUCCESS); + screen_startup = NO; + +/* + * Calculate how many lines are available for display of the edit buffer. + */ + buffer_li = term_lines - SCREEN_RESERVED_LINES; + buffer_half_li = ((buffer_li + 1) & ~1) / 2; + +/* + * Allocate memory for the saved screen structure. This is used to remember + * what is currently showing on the terminal. Since we just cleared the screen, + * we set the current contents of this to be all spaces (i.e., blank). + */ + i = sizeof(struct screen_line) * term_lines; + saved_screen = (struct screen_line *)tec_alloc(TYPE_C_SCR,i); + if(saved_screen == NULL) return(FAIL); + + bzero(saved_screen,i); + screen_sequence = 1; + + for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){ + lp->scr_magic = MAGIC_SCREEN; + lp->companion = NULL; + lp->sequence = 0; + lp->buffer = (short *) + tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * term_columns)); + sp = lp->buffer; + for(j = 0; j < term_columns; j++,sp++) *sp = ' '; + }/* End FOR */ + + saved_screen_valid = 1; + +/* + * Allocate the initial window structure + */ + window_list = wptr = + create_window(term_columns,term_lines - SCREEN_RESERVED_LINES); + curwin = wptr; + wptr->win_buffer = curbuf; + + lp = saved_screen; + lp += term_lines - SCREEN_RESERVED_LINES - 1; + wptr->win_label_line.fmt_saved_line = lp; + lp->companion = &wptr->win_label_line; +/* + * Now we set up the structures for our special lines which form the bottom + * of the display screen. + */ + echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL; + echo_line.fmt_buffer_size = message_line.fmt_buffer_size = term_columns; + + echo_line.fmt_buffer = (short *) + tec_alloc( + TYPE_C_SCRBUF, + (int)(sizeof(short) * echo_line.fmt_buffer_size) + ); + message_line.fmt_buffer = (short *) + tec_alloc( + TYPE_C_SCRBUF, + (int)(sizeof(short)*message_line.fmt_buffer_size) + ); + + for(i = 0; i < term_columns; i++){ + message_line.fmt_buffer[i] = ' '; + echo_line.fmt_buffer[i] = ' '; + }/* End FOR */ + + echo_line.fmt_buffer[0] = '*'; + echo_line.fmt_sequence = message_line.fmt_sequence = 1; + echo_line.fmt_next_line = message_line.fmt_next_line = NULL; + + message_line.fmt_visible_line_position = + echo_line.fmt_visible_line_position = -1; + + lp = saved_screen; + lp += term_lines - SCREEN_RESERVED_LINES; + + message_line.fmt_permanent = 1; + message_line.fmt_saved_line = lp; + message_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE; + lp->companion = &message_line; + + lp += 1; + echo_line.fmt_permanent = 1; + echo_line.fmt_saved_line = lp; + echo_line.fmt_magic = MAGIC_FORMAT_LOOKASIDE; + lp->companion = &echo_line; + + screen_label_line(curbuf," TECO",LABEL_C_TECONAME); + + return(SUCCESS); + +}/* End Routine */ + + + +/* SCREEN_RESIZE - Cause the screen to be refreshed at a new size + * + * Function: + * + * This function is called when the size of the terminal screen has + * changed. This is common on a windowing terminal. This routine has + * to clean up the existing screen database and re-initialize it. + */ +void +screen_resize() +{ +register int i; +register struct window *wptr; +register struct screen_line *lp; + +#ifdef SUN_STYLE_WINDOW_SIZING + struct winsize os_window; +#endif + + PREAMBLE(); + +/* + * The screen_gc_format_line is going to scavenge all the lines because + * we're making them look not-used here. However, we still go ahead and + * clobber the line_buffer's pointer, because we don't like the idea of + * having hanging pointers pointed at our screen lines. + */ + if(saved_screen_valid){ + for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){ + if(lp->companion){ + if(lp->companion->fmt_saved_line != lp){ + fprintf( + stderr, + "lp->companion 0x%x != ->fmt_saved_line 0x%x\n", + (unsigned int)lp->companion, + (unsigned int)lp->companion->fmt_saved_line + ); + CAUSE_BUS_ERROR(); + }/* End IF */ + lp->companion->fmt_saved_line = NULL; + lp->companion = NULL; + }/* End IF */ + }/* End FOR */ + }/* End IF */ + + screen_gc_format_lines(); + +/* + * Return the memory used for the saved screen structure. This structure + * is the one used to remember what is currently showing on the terminal. + */ + if(saved_screen_valid){ + saved_screen_valid = 0; + for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){ + if(lp->buffer) tec_release(TYPE_C_SCRBUF,(char *)lp->buffer); + lp->buffer = NULL; + }/* End FOR */ + + if(saved_screen) tec_release(TYPE_C_SCR,(char *)saved_screen); + saved_screen = NULL; + + }/* End IF */ + +/* + * Reclaim all the window structures + */ + while(1){ + wptr = window_list; + if(wptr == NULL) break; + window_list = wptr->win_next_window; +/* + * Release any memory used to hold the strings in the label line + */ + for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){ + if(wptr->win_label_field_contents[i]){ + tec_release( + TYPE_C_LABELFIELD, + wptr->win_label_field_contents[i] + ); + wptr->win_label_field_contents[i] = NULL; + }/* End IF */ + }/* End FOR */ +/* + * Release the buffer used to hold formatted data + */ + tec_release(TYPE_C_SCRBUF,(char *)wptr->win_label_line.fmt_buffer); + tec_release(TYPE_C_WINDOW,(char *)wptr); + + }/* End While */ + + window_list = NULL; + + if(echo_line.fmt_buffer){ + tec_release(TYPE_C_SCRBUF,(char *)echo_line.fmt_buffer); + }/* End IF */ + + if(message_line.fmt_buffer){ + tec_release(TYPE_C_SCRBUF,(char *)message_line.fmt_buffer); + }/* End IF */ + echo_cnt = 0; + + echo_line.fmt_buffer_line = message_line.fmt_buffer_line = NULL; + echo_line.fmt_buffer_size = message_line.fmt_buffer_size = 0; + +/* + * If the OS supports window size ioctls, we try for that since it + * will tend to be more correct for a window environment. + */ +#ifdef SUN_STYLE_WINDOW_SIZING + if(ioctl(tty_output_chan,TIOCGWINSZ,&os_window) >= 0){ + term_lines = os_window.ws_row; + term_columns = os_window.ws_col; + }/* End IF */ +#endif + if(forced_height > 0) term_lines = forced_height; + if(forced_width > 0) term_columns = forced_width; + + if(term_lines == 0) term_lines = 24; + if(term_columns == 0) term_columns = 80; + + resize_flag = NO; + screen_startup = YES; + screen_init(); + buff_switch(curbuf,0); + parser_reset_echo(); + screen_echo('\0'); + screen_format_windows(); + +}/* End Routine */ + + + +/* SCREEN_FINISH - Here when we are exiting the editor + * + * Function: + * + * This routine is called when TECO is being exited to give us a chance + * to cleanly leave the screen package. We put the cursor at the bottom + * of the screen, send any termination escape sequences that are required + * and flush it all out. + */ +void +screen_finish() +{ + + term_finish(); + +}/* End Routine */ + + + +/* SCREEN_REFRESH - Redraw the screen based on new database + * + * Function: + * + * This routine gets called after the new companion entries have been set + * up for the saved_screen database. Therefore, we can run through the + * lines and update only those which have changed. + */ +void +screen_refresh() +{ +register int x,y; +register struct screen_line *lp; +register struct format_line *his_lp; +register short *sp,*osp; +char pos_flag; + + PREAMBLE(); + + input_pending_flag = tty_input_pending(); + if(input_pending_flag == YES) return; + +/* + * First try to optimize output by performing insert/delete line operations + */ + if(insert_delete_line_capability){ + screen_optimize_lines(); + }/* End IF */ +/* + * Go through each line and insure that the screen appearence is changed to + * match those described by the line buffer structures. + */ + for(y = 0; y < term_lines; y++){ + if(term_speed <= 2400){ + term_flush(); + }/* End IF */ + + input_pending_flag = tty_input_pending(); + if(input_pending_flag == YES) break; + +/* + * Get pointers to the saved screen entry which tells us what the screen + * currently looks like, and pointers to the line buffer structure which + * tells us what the screen ought to look like when we are done. + */ + lp = saved_screen + y; + osp = lp->buffer; + his_lp = lp->companion; +/* + * If there is no line buffer structure, the line ought to appear blank on + * the screen. If it already is blank, no sweat, otherwise we want to do + * a clear to end of line function. If the saved_screen sequence number + * is zero, this is a flag that the line is already clear. + */ + if(his_lp == NULL){ + if(lp->sequence == 0) continue; + for(x = 0; x < term_columns; x++,osp++){ + if((*osp & SCREEN_M_DATA) != ' '){ + if(cury != y || curx > x) term_goto(x,y); + if(cur_mode & SCREEN_M_REVERSE){ + term_standend(); + cur_mode &= ~SCREEN_M_REVERSE; + }/* End IF */ + term_clrtoeol(); + while(x < term_columns){ + *osp++ = ' '; + x += 1; + }/* End While */ + }/* End IF */ + }/* End FOR */ + lp->sequence = 0; + continue; + }/* End IF */ +/* + * If the line buffer sequence number is less than that of the saved screen + * sequence number, we know that this line has not been changed, and thus we + * don't even have to check to see whether they match. + */ + if(his_lp->fmt_sequence <= lp->sequence) continue; + lp->sequence = screen_sequence; + sp = his_lp->fmt_buffer; + his_lp->fmt_visible_line_position = y; +/* + * Well, changes have been made, so we have to go through the line on a byte + * by byte basis comparing character positions. + */ + for(x = 0; x < term_columns; x++,sp++,osp++){ +/* + * If the characters are the same we don't have to take any special action + * unless it is a newline in which case sometimes we have special things to do + */ + if((*sp & SCREEN_M_DATA) != '\n' && *osp == *sp){ + continue; + }/* End If */ +/* + * If it IS a newline, we want to make sure that the screen shows blanks + * for the rest of this line. If it does not, we have to do a clear to + * end of line operation and update the saved screen database to reflect + * this. + */ + if((*sp & SCREEN_M_DATA) == '\n'){ + for(; x < term_columns; x++,osp++){ + if(*osp == ' ') continue; + if(curx != x || cury != y) term_goto(x,y); + if(cur_mode & SCREEN_M_REVERSE){ + term_standend(); + cur_mode &= ~SCREEN_M_REVERSE; + }/* End IF */ + term_clrtoeol(); + for(; x < term_columns; x++){ + *osp++ = ' '; + }/* End FOR */ + }/* End FOR */ + break; + }/* End IF */ +/* + * Copy the byte into our database to reflect the new appearance of the screen + */ + *osp = *sp; + pos_flag = 1; +/* + * If the current position of the cursor is not the position of the character + * we want to update, we have to position to that place. If we are on the + * wrong line, or if we are already to the right of the desired position, + * we have to do a full position escape sequence. + */ + if(cury != y || curx > x){ + term_goto(x,y); + pos_flag = 0; + }/* End IF */ +/* + * If we are not positioned yet, check to see if we are only a few positions + * to the left of where we want to be. If this is the case, it is faster to + * output the actual characters than to generate a full cursor motion escape + * sequence. + */ + if(pos_flag && x - curx < 5){ + while(curx < x){ + if((his_lp->fmt_buffer[curx] & SCREEN_M_REVERSE) != + (cur_mode & SCREEN_M_REVERSE)) break; + term_putc(his_lp->fmt_buffer[curx] & SCREEN_M_DATA); + curx++; + }/* End While */ + pos_flag = 0; + }/* End IF */ +/* + * If we get to here and the position is not correct, no shortcuts helped so + * we have to output the full escape sequence to position the cursor + */ + if(curx != x || cury != y) term_goto(x,y); +/* + * Check to see that we are in the correct mode (standout or not). If not, + * we have to change it before we output the data byte + */ + if((*sp & SCREEN_M_REVERSE) != (cur_mode & SCREEN_M_REVERSE)){ + if(*sp & SCREEN_M_REVERSE){ + term_standout(); + cur_mode |= SCREEN_M_REVERSE; + }/* End IF */ + else { + term_standend(); + cur_mode &= ~SCREEN_M_REVERSE; + }/* End Else */ + }/* End IF */ +/* + * Output the data byte and record that that changes the current x position + */ + term_putc(*sp & SCREEN_M_DATA); + curx++; + + }/* End FOR */ + }/* End FOR */ + +/* + * We don't want to leave the terminal in reverse video between screen updates, + * so if we are in reverse video mode, put it back to non-reverse. + */ + if(cur_mode & SCREEN_M_REVERSE){ + term_standend(); + cur_mode &= ~SCREEN_M_REVERSE; + }/* End IF */ + +/* + * Make sure that the cursor gets left in the position that indicates the + * "dot" position in the edit buffer. + */ + if(curx != cursor_x || cury != cursor_y){ + term_goto(cursor_x,cursor_y); + }/* End IF */ + +/* + * Force this all to be written to the terminal, and increment the sequence + * number + */ + term_flush(); + screen_gc_format_lines(); + screen_sequence += 1; + +}/* End Routine */ + + + +/* SCREEN_OPTIMIZE_LINES - Optimize output with insert/delete line operations + * + * Function: + * + * This routine looks for left over mappings between our saved screen + * array and the formatted format_line structures. Old mappings can + * tell us how lines that are still going to be visible have moved. + */ +void +screen_optimize_lines() +{ +register int y; +int vlp; +register struct screen_line *lp; +int blk_delete_count; +int blk_insert_count; +int companion_count; +int offset; + + PREAMBLE(); + + companion_count = 0; + +/* + * Search for a line which is known to be out of place + */ + for(y = 0; y < buffer_li; y++){ + lp = &saved_screen[y]; + if(lp->companion == NULL) continue; + if((vlp = lp->companion->fmt_visible_line_position) == -1) continue; + if(vlp != y) break; + companion_count += 1; + break; + }/* End FOR */ + + if(y == buffer_li){ + if(companion_count == 0) screen_optimize_by_pattern_matching(); + return; + }/* End IF */ + +/* + * Search for delete line opportunities + */ + offset = 0; + for(y = 0; y < term_lines; y++){ + lp = &saved_screen[y]; + if(lp->companion == NULL) continue; + if((vlp = lp->companion->fmt_visible_line_position) == -1) continue; + vlp += offset; + if(vlp == y) continue; + + if(vlp > y){ + blk_delete_count = vlp - y; + term_delete_line(y-offset,blk_delete_count); + screen_account_for_delete_line(y-offset,blk_delete_count); + continue; + }/* End IF */ + + if(vlp < y){ + offset += y - vlp; + }/* End IF */ + + }/* End FOR */ +/* + * Search for insert line opportunities + */ + for(y = 0; y < term_lines; y++){ + lp = &saved_screen[y]; + if(lp->companion == NULL) continue; + if((vlp = lp->companion->fmt_visible_line_position) == -1) continue; + if(vlp == y) continue; + + if(vlp > y){ + void screen_message(); + screen_message("insert_line confused"); + }/* End IF */ + + if(vlp < y){ + blk_insert_count = y - vlp; + term_insert_line(vlp,blk_insert_count); + screen_account_for_insert_line(vlp,blk_insert_count); + }/* End IF */ + + }/* End FOR */ + +}/* End Routine */ + + + +/* SCREEN_ACCOUNT_FOR_DELETE_LINE - Update screen database + * + * Function: + * + * This routine is called when a delete line sequence is sent to + * the terminal. It adjusts the screen database to reflect what + * has happened to the terminal. + */ +void +screen_account_for_delete_line(y,count) +int y; +int count; +{ +register int i,x; +register struct screen_line *top_lp,*bottom_lp; +register struct format_line *slp; +register short *sp; + + PREAMBLE(); + +/* + * The result of a delete line operation will be that starting at the + * location it was issued, lines will be replaced by the contents of + * lines lower down on the screen. + */ + top_lp = &saved_screen[y]; + bottom_lp = top_lp + count; + + for(i = y + count; i < term_lines; i++,top_lp++,bottom_lp++){ + sp = top_lp->buffer; + top_lp->buffer = bottom_lp->buffer; + bottom_lp->buffer = sp; + + top_lp->sequence = bottom_lp->sequence; + }/* End FOR */ + + top_lp = saved_screen; + for(i = 0; i < term_lines; i++,top_lp++){ + if((slp = top_lp->companion) == NULL) continue; + if(slp->fmt_visible_line_position >= y){ + slp->fmt_visible_line_position -= count; + }/* End IF */ + if(slp->fmt_visible_line_position < 0){ + slp->fmt_visible_line_position = -1; + }/* End IF */ + }/* End FOR */ + + top_lp = &saved_screen[term_lines - count]; + for(i = term_lines - count; i < term_lines; i++,top_lp++){ + + top_lp->sequence = 0; + + sp = top_lp->buffer; + for(x = 0; x < term_columns; x++){ + *sp++ = ' '; + }/* End FOR */ + + }/* End FOR */ + +}/* End Routine */ + +/* SCREEN_ACCOUNT_FOR_INSERT_LINE - Update screen database + * + * Function: + * + * This routine is called when an insert line sequence is sent to + * the terminal. It adjusts the screen database to reflect what + * has happened to the terminal appearence. + */ +void +screen_account_for_insert_line(y,count) +int y; +int count; +{ +register int i,x; +register struct screen_line *top_lp,*bottom_lp; +register struct format_line *slp; +register short *sp; + + PREAMBLE(); + + top_lp = &saved_screen[term_lines - count - 1]; + bottom_lp = &saved_screen[term_lines - 1]; + + for(i = term_lines - count - 1; i >= y; i--,top_lp--,bottom_lp--){ + sp = bottom_lp->buffer; + bottom_lp->buffer = top_lp->buffer; + top_lp->buffer = sp; + + bottom_lp->sequence = top_lp->sequence; + + }/* End FOR */ + + top_lp = saved_screen; + for(i = 0; i < term_lines; i++,top_lp++){ + if((slp = top_lp->companion) == NULL) continue; + if(slp->fmt_visible_line_position >= y){ + slp->fmt_visible_line_position += count; + }/* End IF */ + if(slp->fmt_visible_line_position >= term_lines){ + slp->fmt_visible_line_position = -1; + }/* End IF */ + }/* End FOR */ + + top_lp = &saved_screen[y]; + for(i = 0; i < count; i++,top_lp++){ + + top_lp->sequence = 0; + + sp = top_lp->buffer; + for(x = 0; x < term_columns; x++){ + *sp++ = ' '; + }/* End FOR */ + }/* End FOR */ + +}/* End Routine */ + + + +/* SCREEN_OPTIMIZE_BY_PATTERN_MATCHING - More insert/delete line optimizations + * + * Function: + * + * This routine is called previous to the brute force screen repaint, + * but after the first crack at insert/delete line optimization. This + * routine looks for patterns of lines which indicate insert/delete + * line can save us some output. + */ +void +screen_optimize_by_pattern_matching() +{ +register int x,y,i,c; +register struct screen_line *lp; +register struct format_line *his_lp; +register short *sp,*osp; +char stuff_flag; +int old_check[SCREEN_MAX_LINES],new_check[SCREEN_MAX_LINES]; +int first_change,last_change; + + PREAMBLE(); + + stuff_flag = 0; + for(y = 0; y < buffer_li; y++){ + +/* + * Get pointers to the saved screen entry which tells us what the screen + * currently looks like, and pointers to the line buffer structure which + * tells us what the screen ought to look like when we are done. + */ + lp = saved_screen + y; + osp = lp->buffer; + his_lp = lp->companion; +/* + * If there is no line buffer structure, the line ought to appear blank on + * the screen. If it already is blank, no sweat, otherwise we want to do + * a clear to end of line function. + */ + new_check[y] = old_check[y] = 0; + if(his_lp && his_lp->fmt_sequence <= lp->sequence) continue; + if(his_lp == NULL && lp->sequence == 0) continue; +/* + * Calculate the checksum for the data currently showing on the screen + */ + for(x = 0; x < term_columns; x++,osp++){ + if((c = *osp & SCREEN_M_DATA) != ' '){ + old_check[y] = (old_check[y] << 1) + c; + stuff_flag = 1; + }/* End IF */ + }/* End FOR */ + + if(his_lp == NULL) continue; + sp = his_lp->fmt_buffer; +/* + * Calculate the checksum for the data which we want to be showing on the + * screen. + */ + for(x = 0; x < term_columns; x++,sp++){ + c = *sp & SCREEN_M_DATA; + if(c == ' ') continue; + if(c == '\n') break; + new_check[y] = (new_check[y] << 1) + c; + stuff_flag = 1; + }/* End FOR */ + + }/* End FOR */ + +/* + * If no changes have been made, just return + */ + if(!stuff_flag) return; + +/* + * Determine which region of the screen contains lines which have changed + */ + first_change = last_change = -1; + for(y = 0; y < buffer_li; y++){ + if(old_check[y] == new_check[y]) continue; + first_change = y; + break; + }/* End FOR */ + +/* + * If checksum arrays seem to be identical or only one line changed, then + * just return and don't attempt further optimization. + */ + if(first_change == -1) return; + + for(y = buffer_li - 1; y >= first_change; y--){ + if(old_check[y] == new_check[y]) continue; + last_change = y; + break; + }/* End FOR */ + + if(first_change == last_change) return; + +/* + * Here is the code which tries for a delete-line optimization + */ + for(i = 1; i < last_change - first_change - 3; i++){ + if(old_check[first_change + i] == new_check[first_change]){ + for(y = first_change; y < last_change - i; y++){ + if(old_check[y + i] != new_check[y]) break; + }/* End FOR */ + if(y == (last_change - i)){ + + term_delete_line(first_change,i); + screen_account_for_delete_line(first_change,i); + term_insert_line(last_change+1-i,i); + screen_account_for_insert_line(last_change+1-i,i); + + return; + + }/* End IF */ + }/* End IF */ + }/* End FOR */ + +/* + * Here is the code which tries for a insert-line optimization + */ + for(i = 1; i < last_change - first_change - 3; i++){ + if(old_check[first_change] == new_check[first_change + i]){ + for(y = first_change; y < last_change - i; y++){ + if(old_check[y] != new_check[y + i]) break; + }/* End FOR */ + + if(y == (last_change - i)){ + term_delete_line(last_change+1-i,i); + screen_account_for_delete_line(last_change+1-i,i); + term_insert_line(first_change,i); + screen_account_for_insert_line(first_change,i); + break; + }/* End IF */ + + }/* End IF */ + }/* End FOR */ + +}/* End Routine */ + + + +/* SCREEN_ECHO - Echo the character + * + * Function: + * + * This routine allows the parser to hand us input characters which need + * to be echoed. The reason this is done here instead of by the normal + * screen formatting routines is that some characters are echoed in a + * different way than they are normally displayed. The most notable + * example is newline which just indicates end of line in the normal + * buffer output, but which gets echoed as '<CR>' when it is an echo + * operation. + */ +void +screen_echo(data) +char data; +{ +register int i; + + PREAMBLE(); + +/* + * If we are getting close to the right edge of the screen, we remove the + * first 3/4 of the echo line and just redisplay the last 1/4 to make room + * for future echo characters. + */ + if(echo_cnt > (term_columns - 4)){ + for(i = 0; i < (term_columns / 4); i++){ + echo_line.fmt_buffer[ 1 + i ] = + echo_line.fmt_buffer[ 1 + echo_cnt - (term_columns / 4) + i ]; + }/* End FOR */ + echo_cnt = i; + for(; i < term_columns-2; i++){ + echo_line.fmt_buffer[ 1 + i ] = ' '; + }/* End FOR */ + }/* End IF */ + +/* + * Dispatch the byte to determine how it should be formatted. NULL is a + * special case which is used to flag the fake cursor position on the + * echo line. This is shown by outputing a reverse-video space to the + * position. + */ + switch(data){ + case '\0': + +/* + * If we are on a high-speed line, do the reverse-video space trick to + * show a non-blinking cursor. + */ + if(term_speed > 1200){ + echo_line.fmt_buffer[ 1 + echo_cnt ] = ' ' | SCREEN_M_REVERSE; + echo_line.fmt_buffer[ 2 + echo_cnt ] = ' '; + echo_line.fmt_buffer[ 3 + echo_cnt ] = '\n'; + }/* End IF */ +/* + * Otherwise, if we are on a low speed line, just insert a carriage return + * so that the rest of the line is shown as blanks. + */ + else { + echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n'; + }/* End IF */ + + echo_line.fmt_sequence = screen_sequence; + return; + + case ESCAPE: + data = '$'; + break; + case '\n': + for(i = 0; i < strlen("<CR>"); i++){ + screen_echo("<CR>"[i]); + }/* End FOR */ + return; + case RUBOUT: + return; +/* + * Tabs are always echoed as a fixed length string of spaces regardless of + * the screen position since normal tab algorithms look weird on the echo + * line. + */ + case '\t': + for(i = 0; i < strlen(" "); i++){ + screen_echo(" "[i]); + }/* End FOR */ + return; + default: + break; + }/* End Switch */ + +/* + * If this is a control character, echo as ^byte + */ + if((unsigned char)data < ' '){ + screen_echo('^'); + screen_echo(data + 'A' - 1); + return; + }/* End IF */ + +/* + * Here if it is a normal printable character. We can just echo the character + * as is with no special stuff going on. + */ + echo_line.fmt_buffer[ 1 + echo_cnt++ ] = data; + echo_line.fmt_sequence = screen_sequence; + +}/* End Routine */ + +/* SCREEN_RESET_ECHO - Reset the echo line + * + * Function: + * + * This routine is called to reset the echo line according to the current + * list of command tokens. This is used after difficult things like rubout + * which would be very difficult to back out of. + */ +void +screen_reset_echo(ct) +register struct cmd_token *ct; +{ + + PREAMBLE(); + + echo_line.fmt_buffer[ 1 + echo_cnt ] = ' '; + + while(echo_cnt){ + echo_cnt -= 1; + echo_line.fmt_buffer[ 1 + echo_cnt ] = ' '; + }/* End While */ + + while(ct){ + if(ct->opcode == TOK_C_INPUTCHAR){ + screen_echo(ct->input_byte); + }/* End IF */ + ct = ct->next_token; + }/* End While */ + + echo_line.fmt_buffer[ 1 + echo_cnt ] = '\n'; + + echo_line.fmt_sequence = screen_sequence; + +}/* End Routine */ + + + +/* SCREEN_MESSAGE - Place a message in the message line + * + * Function: + * + * This routine allows the parser to hand us messages which need to be + * displayed. These can be error messages generated by the parse, or + * they can be output messages from a user generated with the ^A command. + */ +void +screen_message(string) +register char *string; +{ +register short *sp; +register int i; +register int c = 0; +register char *mep; +char multi_echo_buffer[32]; + + PREAMBLE(); + + message_flag = YES; + sp = message_line.fmt_buffer; + i = term_columns; + mep = ""; + + while(*mep || *string){ + while(*mep){ + *sp++ = c = *mep++; + if(i-- <= 0) goto too_wide; + continue; + }/* End While */ + + c = *string++; + + switch(c){ +// case NULL: + case 0: + goto too_wide; + case ESCAPE: + c = '$'; + break; + case '\n': + mep = "<CR>"; + continue; + case RUBOUT: + mep = "<RUBOUT>"; + continue; + case '\t': + mep = " "; + continue; + }/* End Switch */ + + if((unsigned char)c < ' '){ + multi_echo_buffer[0] = '^'; + multi_echo_buffer[1] = c + 'A' - 1; + multi_echo_buffer[2] = '\0'; + mep = multi_echo_buffer; + continue; + }/* End IF */ + + *sp++ = c; + if(i-- <= 0) goto too_wide; + }/* End While */ + +too_wide: + if(c != '\n' && i > 0) *sp++ = '\n'; + message_line.fmt_sequence = screen_sequence; + +}/* End Routine */ + +/* ERROR_MESSAGE - Place an error message in the message line + * + * Function: + * + * This routine allows the parser to hand us error messages + * which need to be displayed. In addition, the BELL character + * is output to get the humans attention. + */ +void +error_message(string) +char *string; +{ + PREAMBLE(); + + screen_message(string); + ring_audible_bell = 1; + +}/* End Routine */ + +/* SCREEN_RESET_MESSAGE - Reset the message line + * + * Function: + * + * This routine is called to reset the message line to null + */ +void +screen_reset_message() +{ +register int i; +register short *sp; + + PREAMBLE(); + + if(message_flag == NO) return; + message_flag = NO; + sp = message_line.fmt_buffer; + + for(i = 0; i < term_columns; i++){ + *sp++ = ' '; + }/* End FOR */ + + message_line.fmt_sequence = screen_sequence; + +}/* End Routine */ + +/* SCREEN_SAVE_CURRENT_MESSAGE - Copy out the current message + * + * Function: + * + * This routine copies out the current message up to a certain length. + */ +void +screen_save_current_message(message_save_buff,message_save_max_length) +char *message_save_buff; +int message_save_max_length; +{ +int i; +short *sp; + + PREAMBLE(); + + sp = message_line.fmt_buffer; + i = term_columns; + if(i >= message_save_max_length) i = message_save_max_length - 1; + while(i-- > 0){ + if((*sp & SCREEN_M_DATA) == '\n') break; + *message_save_buff++ = *sp++ & SCREEN_M_DATA; + } + *message_save_buff = '\0'; + +}/* End Routine */ + + + +/* SCREEN_LABEL_LINE - Routine to change the contents of the label line + * + * Function: + * + * This routine is called to change the label line + */ +int +screen_label_line(buffer,string,field) +struct buff_header *buffer; +char *string; +int field; +{ +register short *sp; +register char *cp; +register int i; +register int column; +register struct window *wptr; + + PREAMBLE(); + +/* + * If the field specified is out of range, punt + */ + if(field < 0 || field >= SCREEN_MAX_LABEL_FIELDS){ + screen_message("Illegal field specified to label line\n"); + return(FAIL); + }/* End IF */ + +/* + * We want to modify the label in any window which is displaying this buffer. + * There could be several windows with the same buffer displayed... + */ + for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){ + if(wptr->win_buffer != buffer) continue; +/* + * If no space has been allocated for this field yet, we need to call the + * allocator. Notice the overkill in that each field gets more than enough + * memory. + */ + if(wptr->win_label_field_contents[field] == NULL){ + wptr->win_label_field_contents[field] = + tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH); + if(wptr->win_label_field_contents[field] == NULL){ + return(FAIL); + }/* End IF */ + *(wptr->win_label_field_contents[field]) = '\0'; + }/* End IF */ +/* + * If the current contents of the field are already what we want, we don't + * have to change a thing. + */ + if(strcmp(string,wptr->win_label_field_contents[field]) == 0) continue; + (void) strcpy(wptr->win_label_field_contents[field],string); +/* + * Figure out which column the field begins on. This changes as fields change, + * so it must be computed each time a change happens. + */ + for(i = 0 , column = 0; i < field; i++){ + if(wptr->win_label_field_contents[i] == NULL) continue; + column += strlen(wptr->win_label_field_contents[i]) + 1; + }/* End FOR */ +/* + * Now we have to update the label line from this column onward until we + * reach the end of the final field. + */ + sp = &wptr->win_label_line.fmt_buffer[column]; + for(i = field; i < SCREEN_MAX_LABEL_FIELDS; i++){ + cp = wptr->win_label_field_contents[i]; + if(cp == NULL) continue; + while(*cp){ + if(column >= term_columns) break; + *sp++ = *cp++ | SCREEN_M_REVERSE; + column += 1; + }/* End While */ + if(column >= term_columns) break; + *sp++ = ' ' | SCREEN_M_REVERSE; + column += 1; + }/* End FOR */ +/* + * Blank fill to the end of the screen incase we shrank the label line. + */ + while(column < term_columns){ +#ifdef TERMCAP + *sp++ = termcap_sg ? '-' : ' ' | SCREEN_M_REVERSE; +#endif +#ifdef TERMINFO + *sp++ = (terminfo_magic_cookie_glitch > 0) ? + '-' : ' ' | SCREEN_M_REVERSE; +#endif + column += 1; + }/* End While */ +/* + * Insure that the next screen refresh knows this line has changed + */ + wptr->win_label_line.fmt_sequence = screen_sequence; + + }/* End FOR */ + + return( SUCCESS ); + +}/* End Routine */ + + + +/* SCREEN_LABEL_WINDOW - Routine to set the window number field + * + * Function: + * + * This routine is called to modify the TECONAME field of the + * label lines to reflect the window number. + */ +int +screen_label_window() +{ +register short *sp; +register char *cp; +register int i; +register int column; +register struct window *wptr; +char tmp_buffer[SCREEN_NOMINAL_LINE_WIDTH]; +int field = LABEL_C_TECONAME; + + PREAMBLE(); + +/* + * We want to check the label in every window + */ + for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){ +/* + * If no space has been allocated for this field yet, we need to call the + * allocator. Notice the overkill in that each field gets more than enough + * memory. + */ + if(wptr->win_label_field_contents[field] == NULL){ + wptr->win_label_field_contents[field] = + tec_alloc(TYPE_C_LABELFIELD,SCREEN_NOMINAL_LINE_WIDTH); + if(wptr->win_label_field_contents[field] == NULL){ + return(FAIL); + }/* End IF */ + *(wptr->win_label_field_contents[field]) = '\0'; + }/* End IF */ +/* + * If the current contents of the field are already what we want, we don't + * have to change a thing. + */ + sprintf(tmp_buffer," TECO-%d",wptr->win_window_number); + if(strcmp(tmp_buffer,wptr->win_label_field_contents[field]) == 0){ + continue; + }/* End IF */ + + (void) strcpy(wptr->win_label_field_contents[field],tmp_buffer); + +/* + * Now we have to update the label line from this column onward until we + * reach the end of the final field. + */ + column = 0; + sp = &wptr->win_label_line.fmt_buffer[column]; + for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){ + cp = wptr->win_label_field_contents[i]; + if(cp == NULL) continue; + while(*cp){ + if(column >= term_columns) break; + *sp++ = *cp++ | SCREEN_M_REVERSE; + column += 1; + }/* End While */ + if(column >= term_columns) break; + *sp++ = ' ' | SCREEN_M_REVERSE; + column += 1; + }/* End FOR */ +/* + * Blank fill to the end of the screen incase we shrank the label line. + */ + while(column < term_columns){ + *sp++ = ' ' | SCREEN_M_REVERSE; + column += 1; + }/* End While */ +/* + * Insure that the next screen refresh knows this line has changed + */ + wptr->win_label_line.fmt_sequence = screen_sequence; + + }/* End FOR */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* SCREEN_FORMAT_WINDOWS - Generate display contents for all windows + * + * Function: + * + * This is the main entry point to the screen formatting function. + * It causes screen format_lines to be generated for all the buffers + * that are currently visible on the screen. + */ +void +screen_format_windows() +{ +register struct window *wptr; + + PREAMBLE(); + + for(wptr = window_list; wptr != NULL; wptr = wptr->win_next_window){ + screen_display_window(wptr); + }/* End FOR */ + +}/* End Routine */ + +/* SCREEN_REFORMAT_WINDOWS - Reformat windows (because tab size changed) + * + * Function: + * + * This entry point is called when the format buffers of all the + * displayed windows need to be regenerated. The current case in + * mind is when the user changes the tab stops - this causes all + * the format lines to be obsolete. + */ +void +screen_reformat_windows() +{ +register int i; +register struct screen_line *lp; + + PREAMBLE(); + +/* + * The screen_gc_format_line is going to scavenge all the lines because + * we're making them look not-used here. However, we still go ahead and + * clobber the line_buffer's pointer, because we don't like the idea of + * having hanging pointers pointed at our screen lines. + */ + if(saved_screen_valid){ + for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){ + if(lp->companion){ + if(lp->companion->fmt_saved_line != lp){ + fprintf( + stderr, + "lp->companion 0x%x != ->fmt_saved_line 0x%x\n", + (unsigned int)lp->companion, + (unsigned int)lp->companion->fmt_saved_line + ); + CAUSE_BUS_ERROR(); + }/* End IF */ + if( lp->companion->fmt_permanent ) continue; + lp->companion->fmt_saved_line = NULL; + lp->companion = NULL; + }/* End IF */ + }/* End FOR */ + }/* End IF */ + + screen_gc_format_lines(); + +}/* End Routine */ + +/* SCREEN_DISPLAY_WINDOW - Generate display for a window + * + * Function: + * + * This routine is called to build up the format line descriptor + * structures which represent the edit buffer displayed in the + * specified window. + */ +int +screen_display_window(wptr) +register struct window *wptr; +{ +struct buff_header *hbp; +struct buff_line *tlbp,*blbp; +struct format_line *tsbp,*bsbp; +struct screen_line *sp; +register int i; +int lines; +int anchor_line; +char saw_dot; + + PREAMBLE(); + +/* + * Find a line in our saved screen array that still maps to a buffer location + */ + sp = saved_screen + wptr->win_y_base; + anchor_line = -1; + for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){ + if(sp->companion == NULL) continue; + if(sp->companion->fmt_owning_buffer != wptr->win_buffer) continue; + anchor_line = i; + break; + }/* End FOR */ + + if(anchor_line < 0){ + return(screen_rebuild_from_scratch(wptr)); + }/* End IF */ + +/* + * Now walk the top back + */ + tsbp = sp->companion; + tlbp = tsbp->fmt_buffer_line; + while(i > wptr->win_y_base){ + +/* + * If there is a format line in front of the one we are on, just back + * up to it. + */ + if(tsbp->fmt_prev_line){ + tsbp = tsbp->fmt_prev_line; + i -= 1; + continue; + }/* End IF */ +/* + * Else if there is a buffer line in front of us, get to the final + * format line associated with it. + */ + if(tlbp->prev_line != NULL){ + tlbp = tlbp->prev_line; + tsbp = screen_find_window_format_line(wptr,tlbp); + if(tsbp == NULL){ + if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL); + tsbp = screen_find_window_format_line(wptr,tlbp); + }/* End IF */ + while(tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line; + i -= 1; + continue; + }/* End IF */ +/* + * If we got here, that means that before we could work back up to the old + * top line, we hit the begining of the buffer. Give up. + */ + return(screen_rebuild_from_scratch(wptr)); + + }/* End While */ + +/* + * If we make it to here, we have worked back to the top of the screen, now + * we want to work down to the bottom of the screen. + */ + lines = 1; + blbp = tlbp; + bsbp = tsbp; + +/* + * Determine the format_line that dot lies on so that as we work forward we can + * keep an eye out for it. + */ + hbp = wptr->win_buffer; + buff_find_line(hbp,hbp->dot); + screen_find_dot(wptr); + saw_dot = 0; + + while(lines <= wptr->win_y_size){ + +/* + * If we see the line that dot is on, remember it. The one case when we don't + * do this is if it is the top line of the screen. The reason for this is to + * avoid over-optimizing such that the user makes changes but never sees them + * because the changes are above the top line of the screen. + */ + if(bsbp == wptr->win_dot_format_line && lines != 1) saw_dot = 1; + +/* + * If there is a format line below our current bottom one, simply go + * forward to it. + */ + if(bsbp->fmt_next_line){ + bsbp = bsbp->fmt_next_line; + lines += 1; + continue; + }/* End IF */ +/* + * If we have to go on to the next buffer line, but there is not one there, + * that means that we hit the end of the buffer. + */ + if(blbp->next_line == NULL){ + return(screen_rebuild_from_scratch(wptr)); + }/* End IF */ +/* + * Else, if there is another buffer line after this, go to the first + * format line associated with it. + */ + if(blbp->next_line != NULL){ + blbp = blbp->next_line; + bsbp = screen_find_window_format_line(wptr,blbp); + if(bsbp == NULL){ + if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL); + bsbp = screen_find_window_format_line(wptr,blbp); + }/* End IF */ + lines += 1; + }/* End IF */ + + }/* End While */ + +/* + * Test to see whether we ever saw dot. If not, we need to do the scratch + * screen rebuild. + */ + if(curwin == wptr && saw_dot == 0){ + return(screen_rebuild_from_scratch(wptr)); + }/* End IF */ + +/* + * Now we can start setting up the screen appearance + */ + sp = saved_screen + wptr->win_y_base; + + for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){ + if(tlbp == NULL){ + if(sp->companion) sp->companion->fmt_saved_line = NULL; + sp->companion = NULL; + continue; + }/* End IF */ + + if(sp->companion != tsbp){ + if(sp->companion) sp->companion->fmt_saved_line = NULL; + sp->companion = NULL; + if(tsbp->fmt_saved_line) tsbp->fmt_saved_line->companion = NULL; + tsbp->fmt_saved_line = NULL; + sp->companion = tsbp; + tsbp->fmt_saved_line = sp; + tsbp->fmt_sequence = screen_sequence; + }/* End IF */ + + if(curwin == wptr && tsbp == wptr->win_dot_format_line){ + cursor_y = i; + cursor_x = wptr->win_dot_screen_offset; + }/* End IF */ + + tsbp = tsbp->fmt_next_line; + if(tsbp == NULL){ + tlbp = tlbp->next_line; + if(tlbp){ + tsbp = screen_find_window_format_line(wptr,tlbp); + }/* End IF */ + }/* End IF */ + }/* End FOR */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* SCREEN_REBUILD_FROM_SCRATCH - Things have changed a lot, just rebuild it + * + * Function: + * + * This routine is called to build up the format line descriptor + * structures which represent the current edit buffer. + */ +int +screen_rebuild_from_scratch(wptr) +register struct window *wptr; +{ +struct buff_header *hbp; +struct buff_line *tlbp,*blbp; +struct format_line *tsbp,*bsbp; +struct screen_line *scrbp; +register int i; +int lines,olines; + + PREAMBLE(); + +/* + * Find the buffer line that dot is on, and then the format_line that + * it is on. + */ + hbp = wptr->win_buffer; + tlbp = blbp = buff_find_line(hbp,hbp->dot); + if(screen_find_dot(wptr) == FAIL) return(FAIL); + tsbp = bsbp = wptr->win_dot_format_line; + + lines = 1; + +/* + * Now walk the top back and the bottom forward + */ + while(lines < wptr->win_y_size){ + olines = lines; + +/* + * If there is a format_line in front of the one we are on, just back + * up to it. + */ + if(tsbp->fmt_prev_line){ + tsbp = tsbp->fmt_prev_line; + lines += 1; + }/* End IF */ + +/* + * Else if there is a buffer line in front of us, get to the final + * format_line associated with it. + */ + else if(tlbp->prev_line != NULL){ + tlbp = tlbp->prev_line; + tsbp = screen_find_window_format_line(wptr,tlbp); + if(tsbp == NULL){ + if(screen_format_buff_line(wptr,tlbp) == FAIL) return(FAIL); + tsbp = screen_find_window_format_line(wptr,tlbp); + }/* End IF */ + while(tsbp && tsbp->fmt_next_line) tsbp = tsbp->fmt_next_line; + lines += 1; + }/* End IF */ + +/* + * If there is a format_line below our current bottom one, simply go + * forward to it. + */ + if(bsbp->fmt_next_line){ + bsbp = bsbp->fmt_next_line; + lines += 1; + }/* End IF */ + +/* + * Else, if there is another buffer line after this, go to the first + * format_line associated with it. + */ + else if(blbp->next_line != NULL){ + blbp = blbp->next_line; + bsbp = screen_find_window_format_line(wptr,blbp); + if(bsbp == NULL){ + if(screen_format_buff_line(wptr,blbp) == FAIL) return(FAIL); + bsbp = screen_find_window_format_line(wptr,blbp); + }/* End IF */ + lines += 1; + }/* End IF */ + +/* + * If we havn't made any progress, then the buffer is smaller than the + * screen, so just display it. + */ + if(lines == olines) break; + + }/* End While */ + +/* + * Now we can start setting up the screen appearance + */ + scrbp = saved_screen + wptr->win_y_base; + + for(i = wptr->win_y_base; i < wptr->win_y_end; i++,scrbp++){ + if(tlbp == NULL){ + if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL; + scrbp->companion = NULL; + continue; + }/* End IF */ + + if(scrbp->companion != tsbp){ + if(scrbp->companion) scrbp->companion->fmt_saved_line = NULL; + scrbp->companion = NULL; + if(tsbp->fmt_saved_line){ + tsbp->fmt_saved_line->companion = NULL; + tsbp->fmt_saved_line = NULL; + }/* End IF */ + scrbp->companion = tsbp; + tsbp->fmt_saved_line = scrbp; + tsbp->fmt_sequence = screen_sequence; + }/* End IF */ + + if(curwin == wptr && tsbp == wptr->win_dot_format_line){ + cursor_y = i; + cursor_x = wptr->win_dot_screen_offset; + }/* End IF */ + + tsbp = tsbp->fmt_next_line; + if(tsbp == NULL){ + tlbp = tlbp->next_line; + if(tlbp){ + tsbp = screen_find_window_format_line(wptr,tlbp); + if(tsbp == NULL){ + if(screen_format_buff_line(wptr,tlbp) == FAIL){ + return(FAIL); + }/* End IF */ + tsbp = screen_find_window_format_line(wptr,tlbp); + }/* End IF */ + }/* End IF */ + }/* End IF */ + }/* End FOR */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* SCREEN_SCROLL - Move the current screen up or down specified # lines + * + * Function: + * + * Here in response to the ES command. The user can scroll the screen + * up and down using this command. The current edit position does not + * move, so it is pointless to move it such that 'dot' moves off of the + * screen. + */ +void +screen_scroll(count) +int count; +{ +struct window *wptr = curwin; +struct screen_line *sp; +register int i; + + PREAMBLE(); + + i = count; + if(i < 0) i = -i; + if(i > wptr->win_y_end) return; + +/* + * If the scroll is forward, move the saved lines up in the array by count + */ + if(count > 0){ + sp = &saved_screen[wptr->win_y_base]; + for(i = wptr->win_y_base; i < wptr->win_y_end - count; i++,sp++){ + if(sp->companion) sp->companion->fmt_saved_line = NULL; + sp->companion = (sp+count)->companion; + (sp+count)->companion = NULL; + if(sp->companion == NULL) continue; + sp->companion->fmt_saved_line = sp; + sp->companion->fmt_sequence = screen_sequence; + }/* End FOR */ + sp = &saved_screen[wptr->win_y_end - count]; + for(i = wptr->win_y_end - count; i < wptr->win_y_end; i++,sp++){ + if(sp->companion == NULL) continue; + sp->companion->fmt_saved_line = NULL; + sp->companion = NULL; + }/* End FOR */ + }/* End IF */ +/* + * Else, if the scroll is backward, move the saved lines down in the array + */ + else if(count < 0){ + sp = &saved_screen[wptr->win_y_end - 1]; + for(i = wptr->win_y_end - 1; i >= wptr->win_y_base - count; i--,sp--){ + if(sp->companion) sp->companion->fmt_saved_line = NULL; + sp->companion = (sp+count)->companion; + (sp+count)->companion = NULL; + if(sp->companion == NULL) continue; + sp->companion->fmt_saved_line = sp; + sp->companion->fmt_sequence = screen_sequence; + }/* End FOR */ + sp = &saved_screen[wptr->win_y_base]; + for(i = wptr->win_y_base; i < wptr->win_y_base - count; i++,sp++){ + if(sp->companion == NULL) continue; + sp->companion->fmt_saved_line = NULL; + sp->companion = NULL; + }/* End FOR */ + }/* End IF */ + +}/* End Routine */ + + + +/* SCREEN_GC_FORMAT_LINES - Garbage collect unused format line structures + * + * Function: + * + * This routine determines which format structures are in use and + * deallocates the rest of them so that we don't use an excessive + * number of them. + */ +void +screen_gc_format_lines() +{ +register struct format_line *sbp,*tsbp; +register struct screen_line *sp; +register struct buff_line *lbp; +register int i; +struct window *wptr; + + PREAMBLE(); + +/* + * First mark all the format_line structures as unused + */ + sbp = format_line_alloc_list; + while(sbp){ + sbp->fmt_in_use = 0; + sbp = sbp->fmt_next_alloc; + }/* End While */ + +/* + * Now for each displayed line, find the format structure associated with it. + * Then find the line_buffer structure which owns that format structure and + * mark all of the format structures under it as in use. + */ + for(wptr = window_list ; wptr != NULL; wptr = wptr->win_next_window){ + sp = saved_screen + wptr->win_y_base; + for(i = wptr->win_y_base; i < wptr->win_y_end; i++,sp++){ + sbp = sp->companion; + if(sbp == NULL) continue; + if(sbp->fmt_permanent) continue; + if(sbp->fmt_buffer_line == NULL) continue; + sbp = + screen_find_window_format_line( + wptr, + sbp->fmt_buffer_line + ); + if(sbp == NULL) continue; + while(sbp){ + sbp->fmt_in_use = 1; + sbp = sbp->fmt_next_line; + }/* End While */ + }/* End FOR */ + }/* End FOR */ +/* + * Now go back and deallocate any format_line structures which are not in use + */ + sbp = format_line_alloc_list; + tsbp = NULL; + while(sbp){ + if(sbp->fmt_in_use){ + tsbp = sbp; + sbp = sbp->fmt_next_alloc; + continue; + }/* End IF */ + + lbp = sbp->fmt_buffer_line; + + screen_free_window_format_lines(sbp->fmt_window_ptr,lbp); + + sbp = tsbp ? tsbp : format_line_alloc_list; + + }/* End While */ + +}/* End Routine */ + + + +/* SCREEN_FREE_WINDOW_FORMAT_LINES - Deallocate a window's format lines + * + * Function: + * + * This routine is called to deallocate the list of format lines + * linked off of a line buffer, if they are associated with the + * specified window. + */ +void +screen_free_window_format_lines(wptr,lbp) +struct window *wptr; +register struct buff_line *lbp; +{ +register struct format_line *sbp,*tsbp; + + PREAMBLE(); + +/* + * There are a couple cases to check for: The first situation is when + * the very first format_line (chained off the buff_line structure) is + * the one we want to clobber. + */ + sbp = lbp->format_line; + if(sbp == NULL) return; + + if(sbp->fmt_window_ptr == wptr){ + format_lines_invalid = 1; + tsbp = sbp->fmt_next_window; + lbp->format_line = tsbp; + if(tsbp) tsbp->fmt_prev_window = NULL; + sbp->fmt_next_window = NULL; + screen_free_format_lines(sbp); + format_lines_invalid = 0; + return; + }/* End IF */ + +/* + * Here if it's not the head of the list. We want to keep the forward + * and backward pointers consistent, so we need to treat this case a + * little differently. + */ + while((tsbp = sbp->fmt_next_window)){ + if(tsbp->fmt_window_ptr == wptr){ + format_lines_invalid = 1; + sbp->fmt_next_window = tsbp->fmt_next_window; + if(tsbp->fmt_next_window){ + tsbp->fmt_next_window->fmt_prev_window = sbp; + }/* End IF */ + + tsbp->fmt_next_window = tsbp->fmt_prev_window = NULL; + screen_free_format_lines(tsbp); + format_lines_invalid = 0; + return; + }/* End IF */ + + sbp = tsbp; + + }/* End While */ + +}/* End Routine */ + + + +/* SCREEN_FREE_FORMAT_LINES - Deallocate a list of format lines + * + * Function: + * + * This routine will clean up all the storage associated with a list of + * format lines. It is called with the address of the head of the list, + * and it will continue on down until the entire list is cleaned up. + * It will also take care of cleaning up any companion pointers from the + * screen array. + */ +void +screen_free_format_lines(sbp) +register struct format_line *sbp; +{ +register struct format_line *osbp; +register struct format_line *next_win; + PREAMBLE(); + +/* + * Loop on down the list till we hit the tail + */ + while(sbp){ + +/* + * If this structure is tied to one of the current screen lines, break the + * connection. + */ + if(sbp->fmt_saved_line){ + if(sbp->fmt_saved_line->companion != sbp){ + fprintf( + stderr, + "sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n", + (unsigned int)sbp->fmt_saved_line, + (unsigned int)sbp->fmt_saved_line->companion + ); + CAUSE_BUS_ERROR(); + }/* End IF */ + sbp->fmt_saved_line->companion = NULL; + sbp->fmt_saved_line = NULL; + }/* End IF */ + + next_win = sbp->fmt_next_window; + if(next_win){ + sbp->fmt_next_window = NULL; + sbp->fmt_prev_window = NULL; + if(sbp->fmt_next_line){ + sbp->fmt_next_line->fmt_next_window = next_win; + }/* End IF */ + }/* End IF */ +/* + * Take it off the head of the list and place it on the free list. + */ + sbp->fmt_magic = MAGIC_FORMAT_LOOKASIDE; + osbp = sbp; + sbp = sbp->fmt_next_line; + if(sbp == NULL) sbp = next_win; + osbp->fmt_next_line = format_line_free_list; + format_line_free_list = osbp; + +/* + * Remove it from the allocated list. This list is used to find all + * the format_line structures which are outstanding at any given time. + */ + if(osbp->fmt_prev_alloc){ + osbp->fmt_prev_alloc->fmt_next_alloc = osbp->fmt_next_alloc; + }/* End IF */ + + else if(format_line_alloc_list == osbp){ + format_line_alloc_list = osbp->fmt_next_alloc; + }/* End Else */ + + if(osbp->fmt_next_alloc){ + osbp->fmt_next_alloc->fmt_prev_alloc = osbp->fmt_prev_alloc; + }/* End IF */ + + osbp->fmt_next_alloc = osbp->fmt_prev_alloc = NULL; + + }/* End While */ + +}/* End Routine */ + +void +screen_check_format_lines(sbp,who) +register struct format_line *sbp; +int who; +{ + + PREAMBLE(); + +/* + * Loop on down the list till we hit the tail + */ + while(sbp){ + +/* + * If this structure is tied to one of the current screen lines, break the + * connection. + */ + if(sbp->fmt_saved_line){ + if(sbp->fmt_saved_line->companion != sbp){ + fprintf( + stderr, + "w%d sbp->fmt_saved_line 0x%x != sbp->...companion 0x%x\n", + who, + (unsigned int)sbp->fmt_saved_line, + (unsigned int)sbp->fmt_saved_line->companion + ); + CAUSE_BUS_ERROR(); + }/* End IF */ + }/* End IF */ + +/* + * Take it off the head of the list and place it on the free list. + */ + sbp = sbp->fmt_next_line; + + }/* End While */ + +} + + +/* SCREEN_DEALLOCATE_FORMAT_LOOKASIDE_LIST + * + * Function: + * + * This routine frees up any format_lines we've cached on our local + * lookaside list. + */ +void +screen_deallocate_format_lookaside_list() +{ +register struct format_line *sbp; + + PREAMBLE(); + + while((sbp = format_line_free_list) != NULL){ + format_line_free_list = sbp->fmt_next_line; + sbp->fmt_magic = 0; + tec_release(TYPE_C_SCREENBUF,(char *)sbp->fmt_buffer); + tec_release(TYPE_C_SCREEN,(char *)sbp); + }/* End While */ + +}/* End Routine */ + + + +/* SCREEN_FIND_WINDOW_FORMAT_LINE - Finds format lines for a particular window + * + * Function: + * + * Each buffer line has chained off of it the format lines which hold + * the data as it appears on the screen. In order to handle multiple + * windows, we need the ability to have a format line per visible + * window. Thus, from the buff_line structure, we chain off a list + * of format lines. Since a buffer line may need many format lines to + * represent it (because it may wrap) we need a list. In addition, + * we want to have one of these lists on a per-window basis, so we + * have a pointer to the next list of format lines for another window. + */ +struct format_line * +screen_find_window_format_line(wptr,lbp) +struct window *wptr; +struct buff_line *lbp; +{ +struct format_line *sbp; + + PREAMBLE(); + + sbp = lbp->format_line; + while(sbp){ + if(sbp->fmt_window_ptr == wptr) return(sbp); + sbp = sbp->fmt_next_window; + }/* End While */ + + return(NULL); + +}/* End Routine */ + + + +/* SCREEN_FORMAT_BUFF_LINE - Create the format_line structures for a buff line + * + * Function: + * + * This routine is called with the address of a buff_line structure. It + * creates the format_line structures which represent the printed version + * of the data, i.e., ready to be written to the terminal. + */ +int +screen_format_buff_line(wptr,lbp) +struct window *wptr; +struct buff_line *lbp; +{ +register struct format_line *sbp; +register char *cp; +register short *sp; +register unsigned int c; +register int i; +int byte_count; +int current_column; +char *multi_byte_echo; +char expand_buffer[MAXOF(32,MAX_TAB_WIDTH+1)]; + + PREAMBLE(); + +/* + * If we're called with a null lbp, we've run out of memory or something, + * and we just punt + */ + if(lbp == NULL) return(FAIL); + +/* + * If there is an old format structure hanging around (doubtful), make it + * go away. Note that this has been modified to try to deal with the new + * scheme where we have a different set of format structures for each + * window. + */ + if(lbp->format_line){ + screen_free_window_format_lines(wptr,lbp); + }/* End IF */ + + sbp = allocate_format_buffer(wptr,lbp); + if(sbp == NULL) return(FAIL); + + sbp->fmt_next_window = lbp->format_line; + if(sbp->fmt_next_window){ + sbp->fmt_next_window->fmt_prev_window = sbp; + }/* End IF */ + lbp->format_line = sbp; + + sp = sbp->fmt_buffer; + cp = lbp->buffer; + byte_count = lbp->byte_count; + c = ' '; + current_column = 0; + multi_byte_echo = NULL; + + while(byte_count > 0){ + + if(current_column >= term_columns){ + sbp->fmt_next_line = allocate_format_buffer(wptr,lbp); + if(sbp->fmt_next_line == NULL) return(FAIL); + sbp->fmt_next_line->fmt_prev_line = sbp; + sbp = sbp->fmt_next_line; + sp = sbp->fmt_buffer; + current_column = 0; + }/* End IF */ + + if(multi_byte_echo){ + c = *multi_byte_echo++; + if(*multi_byte_echo == '\0') multi_byte_echo = NULL; + }/* End IF */ + + else { + c = *cp++; + byte_count -= 1; + }/* End Else */ + + c &= 0xFF; + + switch(c){ + case '\t': + i = tab_width - ( current_column % tab_width ); + multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i; + break; + case ESCAPE: + multi_byte_echo = "$"; + break; + + case '\n': + *sp++ = c; + current_column = 0; + break; + default: + if(c < ' '){ + if( hide_cr_flag ){ + expand_buffer[0] = ' '; + expand_buffer[1] = '\0'; + } else { + expand_buffer[0] = '^'; + expand_buffer[1] = c + 'A' - 1; + expand_buffer[2] = '\0'; + } + multi_byte_echo = expand_buffer; + continue; + }/* End IF */ +/* + * If the high bit is on, display it as the code with that bit stripped off, + * or as '.' if it is a non-printable character. We display it reverse video + * if the terminal is not a brain damaged 'magic cookie' terminal. + */ + if(eight_bit_output_flag == NO && c >= 128){ + c -= 128; + if(isprint(c)) c |= SCREEN_M_REVERSE; + else c = '.' | SCREEN_M_REVERSE; +#ifdef TERMCAP + if(termcap_sg){ + c &= ~SCREEN_M_REVERSE; + }/* End IF */ +#endif + }/* End IF */ + *sp++ = c; + current_column += 1; + break; + }/* End Switch */ + + }/* End While */ + + if(c != '\n'){ + if(current_column < (term_columns - 1)){ + *sp++ = '\n'; + }/* End IF */ + }/* End IF */ + + return(SUCCESS); + +}/* End Routine */ + + + +/* SCREEN_FIND_DOT - Find position of dot in a format_line + * + * Function: + * + * There are several times that we need to find the format buffer that is + * associated with dot. This routine determines which format_line dot is + * on, and the offset onto that line (in bytes). + */ +int +screen_find_dot(wptr) +struct window *wptr; +{ +register struct buff_line *lbp; +register struct format_line *sbp; +register char *cp; +register unsigned char c; +int i; +int byte_count; +int current_column; +char *multi_byte_echo; +char expand_buffer[32]; +struct buff_header *hbp = wptr->win_buffer; + + PREAMBLE(); + +/* + * Find the line buffer and format buffer structures + */ + lbp = buff_find_line(hbp,hbp->dot); + + sbp = screen_find_window_format_line(wptr,lbp); + if(sbp == NULL){ + if(screen_format_buff_line(wptr,lbp) == FAIL) return(FAIL); + sbp = screen_find_window_format_line(wptr,lbp); + }/* End IF */ + + cp = lbp->buffer; + byte_count = buff_find_offset(hbp,lbp,hbp->dot); + current_column = 0; + + multi_byte_echo = NULL; + + while(byte_count > 0 || multi_byte_echo){ + + if(current_column >= term_columns){ + sbp = sbp->fmt_next_line; + current_column = 0; + }/* End IF */ + + if(multi_byte_echo){ + c = *multi_byte_echo++; + if(*multi_byte_echo == '\0') multi_byte_echo = NULL; + }/* End IF */ + + else { + c = *cp++; + byte_count -= 1; + }/* End Else */ + + switch(c){ + case '\t': + i = tab_width - ( current_column % tab_width ); + multi_byte_echo = tab_expand + MAX_TAB_WIDTH - i; + break; + case ESCAPE: + multi_byte_echo = "$"; + break; + case '\n': + current_column = 0; + break; + default: + if(c < ' '){ + expand_buffer[0] = '^'; + expand_buffer[1] = c + 'A' - 1; + expand_buffer[2] = '\0'; + multi_byte_echo = expand_buffer; + continue; + }/* End IF */ + current_column += 1; + break; + }/* End Switch */ + + }/* End While */ + + wptr->win_dot_format_line = sbp; + wptr->win_dot_screen_offset = current_column; + + return(SUCCESS); + +}/* End Routine */ + + + +/* ALLOCATE_FORMAT_BUFFER - Routine to return a format_line structure + * + * Function: + * + * This routine will return a format_line structure to the caller. It + * first looks on the free list, and if there is not one there, it will + * create one the hard way. + */ +struct format_line * +allocate_format_buffer(wptr,lbp) +struct window *wptr; +struct buff_line *lbp; +{ +register struct format_line *sbp; +struct buff_header *hbp = wptr->win_buffer; + + PREAMBLE(); + + sbp = format_line_free_list; + if(sbp != NULL){ + if(sbp->fmt_buffer_size == term_columns){ + format_line_free_list = sbp->fmt_next_line; + } + else { + screen_deallocate_format_lookaside_list(); + sbp = NULL; + } + }/* End IF */ + + if(sbp == NULL){ + sbp = (struct format_line *) + tec_alloc(TYPE_C_SCREEN,sizeof(struct format_line)); + if(sbp == NULL) return(NULL); + + sbp->fmt_buffer_size = term_columns; + sbp->fmt_buffer = (short *) + tec_alloc( + TYPE_C_SCREENBUF, + (int)(sizeof(short) * sbp->fmt_buffer_size) + ); + if(sbp->fmt_buffer == NULL){ + tec_release(TYPE_C_SCREEN,(char *)sbp); + return(NULL); + }/* End IF */ + }/* End Else */ + + sbp->fmt_magic = MAGIC_FORMAT; + sbp->fmt_owning_buffer = hbp; + sbp->fmt_buffer_line = lbp; + sbp->fmt_sequence = screen_sequence; + sbp->fmt_next_line = sbp->fmt_prev_line = NULL; + sbp->fmt_next_window = sbp->fmt_prev_window = NULL; + sbp->fmt_window_ptr = wptr; + sbp->fmt_saved_line = NULL; + sbp->fmt_visible_line_position = -1; + sbp->fmt_permanent = 0; + +/* + * Place this at the head of the allocated queue + */ + sbp->fmt_next_alloc = format_line_alloc_list; + sbp->fmt_prev_alloc = NULL; + if(sbp->fmt_next_alloc) sbp->fmt_next_alloc->fmt_prev_alloc = sbp; + format_line_alloc_list = sbp; + + return(sbp); + +}/* End Routine */ + + + +/* SCREEN_REDRAW - Cause a redraw of the screen + * + * Function: + * + * This routine is called when the screen may have become corrupted + * and needs to be refreshed. + */ +void +screen_redraw() +{ +register struct screen_line *lp; +register short *sp; +register int i,j; + + PREAMBLE(); + + term_goto(0,0); + term_clrtobot(); + + for(lp = saved_screen, i = 0; i < term_lines; i++,lp++){ + sp = lp->buffer; + for(j = 0; j < term_columns; j++,sp++) *sp = ' '; + lp->sequence = 0; + }/* End FOR */ + + screen_format_windows(); + screen_refresh(); + +}/* End Routine */ + + + +/* CREATE_WINDOW - Create a window data structure + * + * Function: + * + * This function is called to create a window data structure which + * then occupies some portion of the screen. The initial structure + * starts out owning all but the reserved lines, but then lines are + * stolen from it as it is split into other windows. + */ +struct window * +create_window(x_size,y_size) +int x_size; +int y_size; +{ +register struct window *wptr; +register int i; + + PREAMBLE(); + + wptr = (struct window *)tec_alloc(TYPE_C_WINDOW,sizeof(struct window)); + if(wptr == NULL) return(NULL); + + wptr->win_label_line.fmt_buffer = (short *) + tec_alloc(TYPE_C_SCRBUF,(int)(sizeof(short) * x_size)); + if(wptr->win_label_line.fmt_buffer == NULL){ + tec_release(TYPE_C_WINDOW,(char *)wptr); + return(NULL); + }/* End IF */ + + wptr->win_window_number = next_window_number++; + wptr->win_next_window = NULL; + wptr->win_buffer = NULL; +/* + * Initialize the xy size of the window. The y gets set to size-1 to leave + * room for the label line. + */ + wptr->win_x_size = x_size; + wptr->win_y_size = y_size - 1; + wptr->win_y_base = 0; + wptr->win_y_end = wptr->win_y_base + wptr->win_y_size; +/* + * Initialize the label line structure. Each window has one of these in + * reverse video showing which buffer is being displayed. + */ + wptr->win_label_line.fmt_next_line = + wptr->win_label_line.fmt_prev_line = + wptr->win_label_line.fmt_next_alloc = + wptr->win_label_line.fmt_prev_alloc = NULL; + + wptr->win_label_line.fmt_permanent = 1; + wptr->win_label_line.fmt_sequence = 0; + wptr->win_label_line.fmt_owning_buffer = NULL; + wptr->win_label_line.fmt_visible_line_position = -1; + + wptr->win_label_line.fmt_window_ptr = wptr; /* FIX */ + wptr->win_label_line.fmt_buffer_line = NULL; + wptr->win_label_line.fmt_buffer_size = x_size; + + for(i = 0; i < x_size; i++){ + wptr->win_label_line.fmt_buffer[i] = ' ' | SCREEN_M_REVERSE; + }/* End FOR */ + + wptr->win_label_line.fmt_sequence = screen_sequence; + + for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){ + wptr->win_label_field_contents[i] = NULL; + }/* End FOR */ + + return(wptr); + +}/* End Routine */ + + + +/* SCREEN_SPLIT_WINDOW - Make two where there is only one + * + * Function: + * + * This routine is called when the user wants to split the current window + * into two. This allows him to display several buffers simultaneously. + */ +struct window * +screen_split_window(old_wptr,lines,buffer_number) +register struct window *old_wptr; +int lines; +int buffer_number; +{ +register int i; +register struct window *new_wptr; +register struct screen_line *lp; + + PREAMBLE(); + + if(old_wptr->win_y_size < SCREEN_MINIMUM_WINDOW_HEIGHT){ + error_message("Window is already as small as possible"); + return(NULL); + }/* End IF */ + + if(lines < SCREEN_MINIMUM_WINDOW_HEIGHT){ + lines = SCREEN_MINIMUM_WINDOW_HEIGHT; + }/* End If */ + + if((old_wptr->win_y_size - (lines + 1)) < SCREEN_MINIMUM_WINDOW_HEIGHT){ + error_message("Resulting windows would be too small"); + return(NULL); + }/* End IF */ + + new_wptr = create_window(old_wptr->win_x_size,old_wptr->win_y_size-lines); + new_wptr->win_next_window = window_list; + window_list = new_wptr; + new_wptr->win_buffer = old_wptr->win_buffer; +/* + * Take the lines of the new window away from the old window + */ + old_wptr->win_y_size -= new_wptr->win_y_size + 1; +/* + * Have the new window start above the old one + */ + new_wptr->win_y_base = old_wptr->win_y_base; + old_wptr->win_y_base += new_wptr->win_y_size + 1; +/* + * Recalculate the ends of each window + */ + old_wptr->win_y_end = old_wptr->win_y_base + old_wptr->win_y_size; + new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size; +/* + * Initialize the label line structure. Each window has one of these in + * reverse video showing which buffer is being displayed. + */ + for(i = 0; i < new_wptr->win_x_size; i++){ + new_wptr->win_label_line.fmt_buffer[i] = + old_wptr->win_label_line.fmt_buffer[i]; + }/* End FOR */ + + for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){ + if(old_wptr->win_label_field_contents[i]){ + screen_label_line(old_wptr->win_buffer, + old_wptr->win_label_field_contents[i],i); + }/* End IF */ + }/* End FOR */ + + screen_label_window(); + + lp = saved_screen + new_wptr->win_y_end; + if(new_wptr->win_label_line.fmt_saved_line){ + new_wptr->win_label_line.fmt_saved_line->companion = NULL; + }/* End IF */ + new_wptr->win_label_line.fmt_saved_line = lp; + if(lp->companion) lp->companion->fmt_saved_line = NULL; + lp->companion = &new_wptr->win_label_line; + new_wptr->win_label_line.fmt_sequence = screen_sequence; + + if(buffer_number){ + buff_openbuffnum(buffer_number,0); + }/* End IF */ + + return(new_wptr); + +}/* End Routine */ + + + +/* SCREEN_DELETE_WINDOW - Here to delete a window + * + * Function: + * + * This function is called when a user wants to delete a window. + * The space gets given back to the window next to it. + */ +void +screen_delete_window(old_wptr) +register struct window *old_wptr; +{ + +register int i; +register struct window *new_wptr; +register struct screen_line *lp; + + PREAMBLE(); + + if((old_wptr->win_y_size + 1) == (term_lines - SCREEN_RESERVED_LINES)){ + error_message("Illegal to delete the final window"); + return; + }/* End IF */ +/* + * Find the old_wptr on the list of windows, and remove it + */ + if(window_list == old_wptr) window_list = old_wptr->win_next_window; + else { + new_wptr = window_list; + while(new_wptr->win_next_window != old_wptr){ + new_wptr = new_wptr->win_next_window; + }/* End While */ + new_wptr->win_next_window = old_wptr->win_next_window; + }/* End Else */ +/* + * Remove the label line from the screen display + */ + lp = old_wptr->win_label_line.fmt_saved_line; + lp->companion = NULL; + old_wptr->win_label_line.fmt_saved_line = NULL; +/* + * If the window is at the bottom of the screen, we will combine it with + * the window above it. + */ + if((old_wptr->win_y_end + 1) == (term_lines - SCREEN_RESERVED_LINES)){ + new_wptr = window_list; + while(new_wptr){ + if((new_wptr->win_y_end + 1) == old_wptr->win_y_base) break; + new_wptr = new_wptr->win_next_window; + }/* End While */ + if(!new_wptr){ + error_message("Failure to find window above"); + return; + }/* End IF */ + lp = new_wptr->win_label_line.fmt_saved_line; + lp->companion = NULL; + new_wptr->win_y_size += old_wptr->win_y_size + 1; + new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size; + lp = saved_screen + new_wptr->win_y_end; + lp->companion = &new_wptr->win_label_line; + new_wptr->win_label_line.fmt_saved_line = lp; + }/* End IF */ +/* + * Else, we combine it with the window below it + */ + else { + new_wptr = window_list; + while(new_wptr){ + if(new_wptr->win_y_base == (old_wptr->win_y_end + 1)) break; + new_wptr = new_wptr->win_next_window; + }/* End While */ + if(!new_wptr){ + error_message("Failure to find window below"); + return; + }/* End IF */ + lp = new_wptr->win_label_line.fmt_saved_line; + lp->companion = NULL; + new_wptr->win_y_base -= old_wptr->win_y_size + 1; + new_wptr->win_y_size += old_wptr->win_y_size + 1; + new_wptr->win_y_end = new_wptr->win_y_base + new_wptr->win_y_size; + lp = saved_screen + new_wptr->win_y_end; + lp->companion = &new_wptr->win_label_line; + new_wptr->win_label_line.fmt_saved_line = lp; + }/* End IF */ +/* + * Release any memory used to hold the strings in the label line + */ + for(i = 0; i < SCREEN_MAX_LABEL_FIELDS; i++){ + if(old_wptr->win_label_field_contents[i]){ + tec_release( + TYPE_C_LABELFIELD, + old_wptr->win_label_field_contents[i] + ); + old_wptr->win_label_field_contents[i] = NULL; + }/* End IF */ + }/* End FOR */ +/* + * Release the buffer used to hold formatted data + */ + tec_release(TYPE_C_SCRBUF,(char *)old_wptr->win_label_line.fmt_buffer); + tec_release(TYPE_C_WINDOW,(char *)old_wptr); + + curwin = new_wptr; + curbuf = new_wptr->win_buffer; + +}/* End Routine */ + + + +/* WINDOW_SWITCH - Switch to a new window + * + * Function: + * + * This function is called to switch the current window to a different + * window. + */ +int +window_switch(window_number) +int window_number; +{ +register struct window *wptr; + + PREAMBLE(); + +/* + * If the window number is specified as zero, just select the next window + * on the list. + */ + if(window_number == 0){ + if((wptr = curwin->win_next_window) == NULL) wptr = window_list; + window_number = wptr->win_window_number; + }/* End IF */ + + wptr = window_list; +/* + * Search the list until we find the correct window. + */ + while(wptr && wptr->win_window_number != window_number){ + wptr = wptr->win_next_window; + }/* End While */ +/* + * If the search failed, give up. + */ + if(wptr == NULL){ + char tmp_message[LINE_BUFFER_SIZE]; + sprintf(tmp_message,"can't switch to window %d",window_number); + error_message(tmp_message); + return(FAIL); + }/* End IF */ + +/* + * Check to see that we actually found the right window. + */ + if(wptr->win_window_number != window_number) return(FAIL); +/* + * Switch the current window and edit buffer to the newly selected window. + */ + curwin = wptr; + return(SUCCESS); + +}/* End Routine */ |