diff --git a/screen.c b/screen.c index 923d24bba..915d80506 100644 --- a/screen.c +++ b/screen.c @@ -1,3 +1,11 @@ +/** \file screen.c High level library for handling the terminal screen + + The screen library allows the interactive reader to write its + output to screen efficiently by keeping an inetrnal representation + of the current screen contents and trying to find the most + efficient way for transforming that to the desired screen content. +*/ + #include "config.h" #include @@ -43,6 +51,11 @@ #include "highlight.h" #include "screen.h" +/** + Ugly kludge. The internal buffer used to store output of + tputs. Since tputs external function can only take an integer and + not a pointer as parameter we need a static storage buffer. +*/ static buffer_t *s_writeb_buffer=0; /** @@ -186,22 +199,23 @@ static int calc_prompt_width( wchar_t *prompt ) return res; } - +/** + Free all memory used by one line_t struct. +*/ static void free_line( void *l ) { line_t *line = (line_t *)l; -// debug( 0, L"Free line at %d", l); al_destroy( &line->text ); al_destroy( &line->color ); free( line ); } - +/** + Clear the specified array of line_t structs. +*/ static void s_reset_arr( array_list_t *l ) { -// debug( 0, L"I have %d lines", al_get_count( l )); - al_foreach( l, &free_line ); al_truncate( l, 0 ); } @@ -209,18 +223,21 @@ static void s_reset_arr( array_list_t *l ) void s_init( screen_t *s ) { memset( s, 0, sizeof(screen_t)); - sb_init( &s->prompt_buff ); + sb_init( &s->actual_prompt ); } void s_destroy( screen_t *s ) { - s_reset_arr( &s->screen ); - al_destroy( &s->screen ); - s_reset_arr( &s->output ); - al_destroy( &s->output ); + s_reset_arr( &s->actual ); + al_destroy( &s->actual ); + s_reset_arr( &s->desired ); + al_destroy( &s->desired ); } +/** + Allocate a new line_t struct. +*/ static line_t *s_create_line() { line_t *current = malloc( sizeof( line_t )); @@ -229,12 +246,17 @@ static line_t *s_create_line() return current; } -/* - Appends a character to the end of the line that the output cursor is on +/** + Appends a character to the end of the line that the output cursor is + on. This function automatically handles linebreaks and lines longer + than the screen width. */ -static void s_output_append_char( screen_t *s, wchar_t b, int c, int prompt_width ) +static void s_desired_append_char( screen_t *s, + wchar_t b, + int c, + int prompt_width ) { - int line_no = s->output_cursor[1]; + int line_no = s->desired_cursor[1]; switch( b ) { @@ -242,12 +264,12 @@ static void s_output_append_char( screen_t *s, wchar_t b, int c, int prompt_widt { int i; line_t *current = s_create_line(); - al_push( &s->output, current ); - s->output_cursor[1]++; - s->output_cursor[0]=0; + al_push( &s->desired, current ); + s->desired_cursor[1]++; + s->desired_cursor[0]=0; for( i=0; i < prompt_width; i++ ) { - s_output_append_char( s, L' ', 0, prompt_width ); + s_desired_append_char( s, L' ', 0, prompt_width ); } break; } @@ -255,10 +277,10 @@ static void s_output_append_char( screen_t *s, wchar_t b, int c, int prompt_widt case L'\r': { line_t *current; - current = (line_t *)al_get( &s->output, line_no ); + current = (line_t *)al_get( &s->desired, line_no ); al_truncate( ¤t->text, 0 ); al_truncate( ¤t->color, 0 ); - s->output_cursor[0]=0; + s->desired_cursor[0]=0; break; } @@ -270,46 +292,55 @@ static void s_output_append_char( screen_t *s, wchar_t b, int c, int prompt_widt int ew = wcwidth( ellipsis_char ); int i; - current = (line_t *)al_get( &s->output, line_no ); + current = (line_t *)al_get( &s->desired, line_no ); if( !current ) { current = s_create_line(); - al_push( &s->output, current ); + al_push( &s->desired, current ); } - if( s->output_cursor[0] + cw + ew > screen_width ) + /* + Check if we are at the end of the line. If so, print an + ellipsis character and continue on the next line. + */ + if( s->desired_cursor[0] + cw + ew > screen_width ) { al_push_long( ¤t->text, ellipsis_char ); al_push_long( ¤t->color, 0 ); current = s_create_line(); - al_push( &s->output, current ); - s->output_cursor[1]++; - s->output_cursor[0]=0; + al_push( &s->desired, current ); + s->desired_cursor[1]++; + s->desired_cursor[0]=0; for( i=0; i < (prompt_width-ew); i++ ) { - s_output_append_char( s, L' ', 0, prompt_width ); + s_desired_append_char( s, L' ', 0, prompt_width ); } - s_output_append_char( s, ellipsis_char, 0, prompt_width ); + s_desired_append_char( s, ellipsis_char, 0, prompt_width ); } al_push_long( ¤t->text, b ); al_push_long( ¤t->color, c ); - s->output_cursor[0]+= cw; + s->desired_cursor[0]+= cw; break; } } } +/** + The writeb function offered to tputs. +*/ static int s_writeb( char c ) { b_append( s_writeb_buffer, &c, 1 ); return 0; } - +/** + Move screen cursor to the specified position. +*/ static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y ) { int i; @@ -326,7 +357,7 @@ static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y ) output_set_writer( &s_writeb ); s_writeb_buffer = b; - y_steps = new_y - s->screen_cursor[1]; + y_steps = new_y - s->actual_cursor[1]; if( y_steps > 0 && (strcmp( cursor_down, "\n")==0)) { @@ -336,7 +367,7 @@ static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y ) course move the cursor to the beginning of the line as well. The cursor_up does not have this behaviour... */ - s->screen_cursor[0]=0; + s->actual_cursor[0]=0; } if( y_steps < 0 ) @@ -355,14 +386,13 @@ static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y ) } - x_steps = new_x - s->screen_cursor[0]; + x_steps = new_x - s->actual_cursor[0]; if( x_steps && new_x == 0 ) { char c = '\r'; b_append( b, &c, 1 ); x_steps = 0; -// debug( 0, L"return to first" ); } if( x_steps < 0 ){ @@ -379,13 +409,16 @@ static void s_move( screen_t *s, buffer_t *b, int new_x, int new_y ) } - s->screen_cursor[0] = new_x; - s->screen_cursor[1] = new_y; + s->actual_cursor[0] = new_x; + s->actual_cursor[1] = new_y; output_set_writer( writer_old ); } +/** + Set the pen color for the terminal +*/ static void s_set_color( screen_t *s, buffer_t *b, int c ) { @@ -401,19 +434,26 @@ static void s_set_color( screen_t *s, buffer_t *b, int c ) } +/** + Convert a wide character to a multibyte string and append it to the + buffer. +*/ static void s_write_char( screen_t *s, buffer_t *b, wchar_t c ) { int (*writer_old)(char) = output_get_writer(); output_set_writer( &s_writeb ); s_writeb_buffer = b; - s->screen_cursor[0]+=wcwidth( c ); + s->actual_cursor[0]+=wcwidth( c ); writech( c ); output_set_writer( writer_old ); } - +/** + Send the specified string through tputs and append the output to + the specified buffer. +*/ static void s_write_mbs( buffer_t *b, char *s ) { int (*writer_old)(char) = output_get_writer(); @@ -426,7 +466,10 @@ static void s_write_mbs( buffer_t *b, char *s ) output_set_writer( writer_old ); } - +/** + Convert a wide string to a multibyte string and append it to the + buffer. +*/ static void s_write_str( buffer_t *b, wchar_t *s ) { int (*writer_old)(char) = output_get_writer(); @@ -439,7 +482,9 @@ static void s_write_str( buffer_t *b, wchar_t *s ) output_set_writer( writer_old ); } - +/** + Update the screen to match the desired output. +*/ static void s_update( screen_t *scr, wchar_t *prompt ) { int i, j; @@ -448,25 +493,24 @@ static void s_update( screen_t *scr, wchar_t *prompt ) buffer_t output; b_init( &output ); - if( wcscmp( prompt, (wchar_t *)scr->prompt_buff.buff ) ) + if( wcscmp( prompt, (wchar_t *)scr->actual_prompt.buff ) ) { s_move( scr, &output, 0, 0 ); s_write_str( &output, prompt ); - sb_clear( &scr->prompt_buff ); - sb_append( &scr->prompt_buff, prompt ); -// debug( 0, L"YAY %d", prompt_width ); - scr->screen_cursor[0] = prompt_width; + sb_clear( &scr->actual_prompt ); + sb_append( &scr->actual_prompt, prompt ); + scr->actual_cursor[0] = prompt_width; } - for( i=0; i< al_get_count( &scr->output ); i++ ) + for( i=0; i< al_get_count( &scr->desired ); i++ ) { - line_t *o_line = (line_t *)al_get( &scr->output, i ); - line_t *s_line = (line_t *)al_get( &scr->screen, i ); + line_t *o_line = (line_t *)al_get( &scr->desired, i ); + line_t *s_line = (line_t *)al_get( &scr->actual, i ); if( !s_line ) { s_line = s_create_line(); - al_push( &scr->screen, s_line ); + al_push( &scr->actual, s_line ); } for( j=(i==0?prompt_width:0); jtext ); j++ ) { @@ -494,25 +538,24 @@ static void s_update( screen_t *scr, wchar_t *prompt ) } } } -// debug( 0, L"frum frum %d %d", al_get_count( &o_line->text ), al_get_count( &s_line->text ) ); + if( al_get_count( &s_line->text ) > al_get_count( &o_line->text ) ) { s_move( scr, &output, al_get_count( &o_line->text ), i ); s_write_mbs( &output, clr_eol); al_truncate( &s_line->text, al_get_count( &o_line->text ) ); -// debug( 0, L"YAY DELETE from %d", al_get_count( &o_line->text ) ); } } - for( i=al_get_count( &scr->output ); i< al_get_count( &scr->screen ); i++ ) + for( i=al_get_count( &scr->desired ); i< al_get_count( &scr->actual ); i++ ) { - line_t *s_line = (line_t *)al_get( &scr->screen, i ); + line_t *s_line = (line_t *)al_get( &scr->actual, i ); s_move( scr, &output, 0, i ); s_write_mbs( &output, clr_eol); al_truncate( &s_line->text, 0 ); } - s_move( scr, &output, scr->output_cursor[0], scr->output_cursor[1] ); + s_move( scr, &output, scr->desired_cursor[0], scr->desired_cursor[1] ); s_set_color( scr, &output, 0xffffffff); @@ -555,12 +598,12 @@ void s_write( screen_t *s, return; } - s_reset_arr( &s->output ); - s->output_cursor[0] = s->output_cursor[1] = 0; + s_reset_arr( &s->desired ); + s->desired_cursor[0] = s->desired_cursor[1] = 0; for( i=0; ioutput_cursor, sizeof(int)*2); + memcpy(cursor_arr, s->desired_cursor, sizeof(int)*2); col = 0; } - s_output_append_char( s, b[i], col, prompt_width ); + s_desired_append_char( s, b[i], col, prompt_width ); } if( i == cursor ) { - memcpy(cursor_arr, s->output_cursor, sizeof(int)*2); + memcpy(cursor_arr, s->desired_cursor, sizeof(int)*2); } - memcpy( s->output_cursor, cursor_arr, sizeof(int)*2 ); + memcpy( s->desired_cursor, cursor_arr, sizeof(int)*2 ); s_update( s, prompt ); } void s_reset( screen_t *s ) { - s_reset_arr( &s->screen ); - s->screen_cursor[0] = s->screen_cursor[1] = 0; - sb_clear( &s->prompt_buff ); + s_reset_arr( &s->actual ); + s->actual_cursor[0] = s->actual_cursor[1] = 0; + sb_clear( &s->actual_prompt ); } diff --git a/screen.h b/screen.h index 0ae93db23..fa5e32910 100644 --- a/screen.h +++ b/screen.h @@ -1,34 +1,87 @@ +/** \file screen.h High level library for handling the terminal screen + + The screen library allows the interactive reader to write its + output to screen efficiently by keeping an inetrnal representation + of the current screen contents and trying to find the most + efficient way for transforming that to the desired screen content. +*/ #ifndef FISH_SCREEN_H #define FISH_SCREEN_H -#define SCREEN_REPAINT 1 -#define SCREEN_SKIP_RETURN 2 - +/** + The struct representing the current and desired screen contents. +*/ typedef struct { - array_list_t output; - array_list_t screen; - int output_cursor[2]; - int screen_cursor[2]; - string_buffer_t prompt_buff; + /* + The internal representation of the desired screen contents. + */ + array_list_t desired; + /** + The internal representation of the actual screen contents. + */ + array_list_t actual; + /** + The desired cursor position. + */ + int desired_cursor[2]; + /** + The actual cursor position. + */ + int actual_cursor[2]; + /** + A stringbuffer containing the prompt which was last printed to + the screen. + */ + string_buffer_t actual_prompt; } screen_t; +/** + A struct representing a single line of a screen. Consists of two + array_lists, which must always be of the same length. +*/ typedef struct { + /** + The text contents of the line + */ array_list_t text; + /** + Highlight information for the line + */ array_list_t color; } line_t; +/** + Initialize a new screen struct +*/ void s_init( screen_t *s ); + +/** + Free all memory used by the specified screen struct +*/ void s_destroy( screen_t *s ); +/** + This is the main function for the screen putput library. It is used + to define the desired contents of the screen. The screen command + will use it's knowlege of the current contents of the screen in + order to render the desired output using as few terminal commands + as possible. +*/ void s_write( screen_t *s, wchar_t *prompt, - wchar_t *b, - int *c, - int cursor ); + wchar_t *commandline, + int *colors, + int cursor_pos ); + +/** + This function resets the screen buffers internal knowledge about + the contents of the screen. Use this function when some other + function than s_write has written to the screen. +*/ void s_reset( screen_t *s ); #endif