From 2812c7beaff201fb826b84be4d10a21e68df293c Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Thu, 3 Feb 2011 22:18:27 -0500 Subject: [PATCH] Support resize --- termemu.cpp | 53 ++++++++++++++++++++++++++++++++++++++--- terminal.cpp | 12 +++++++++- terminal.hpp | 3 +++ terminalframebuffer.cpp | 49 +++++++++++++++++++++++++++++++++++-- terminalframebuffer.hpp | 4 ++++ 5 files changed, 115 insertions(+), 6 deletions(-) diff --git a/termemu.cpp b/termemu.cpp index 43f3e94..5da8ab4 100644 --- a/termemu.cpp +++ b/termemu.cpp @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "parser.hpp" #include "terminal.hpp" @@ -117,9 +119,31 @@ int main( int argc, void emulate_terminal( int fd, int debug_fd ) { + /* establish WINCH fd and start listening for signal */ + sigset_t signal_mask; + assert( sigemptyset( &signal_mask ) == 0 ); + assert( sigaddset( &signal_mask, SIGWINCH ) == 0 ); + + /* stop "ignoring" WINCH signal */ + assert( sigprocmask( SIG_BLOCK, &signal_mask, NULL ) == 0 ); + + int winch_fd = signalfd( -1, &signal_mask, 0 ); + if ( winch_fd < 0 ) { + perror( "signalfd" ); + return; + } + + /* get current window size */ + struct winsize window_size; + if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCGWINSZ" ); + return; + } + + /* open parser and terminal */ Parser::UTF8Parser parser; - Terminal::Emulator terminal( 80, 24 ); - struct pollfd pollfds[ 2 ]; + Terminal::Emulator terminal( window_size.ws_col, window_size.ws_row ); + struct pollfd pollfds[ 3 ]; pollfds[ 0 ].fd = STDIN_FILENO; pollfds[ 0 ].events = POLLIN; @@ -127,10 +151,13 @@ void emulate_terminal( int fd, int debug_fd ) pollfds[ 1 ].fd = fd; pollfds[ 1 ].events = POLLIN; + pollfds[ 2 ].fd = winch_fd; + pollfds[ 2 ].events = POLLIN; + swrite( STDOUT_FILENO, terminal.open().c_str() ); while ( 1 ) { - int active_fds = poll( pollfds, 2, -1 ); + int active_fds = poll( pollfds, 3, -1 ); if ( active_fds <= 0 ) { perror( "poll" ); break; @@ -144,6 +171,26 @@ void emulate_terminal( int fd, int debug_fd ) if ( termemu( fd, fd, false, debug_fd, &parser, &terminal ) < 0 ) { break; } + } else if ( pollfds[ 2 ].revents & POLLIN ) { + /* resize */ + struct signalfd_siginfo info; + assert( read( winch_fd, &info, sizeof( info ) ) == sizeof( info ) ); + assert( info.ssi_signo == SIGWINCH ); + + /* get new size */ + if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCGWINSZ" ); + return; + } + + /* tell emulator */ + terminal.resize( window_size.ws_col, window_size.ws_row ); + + /* tell child process */ + if ( ioctl( fd, TIOCSWINSZ, &window_size ) < 0 ) { + perror( "ioctl TIOCSWINSZ" ); + return; + } } else if ( (pollfds[ 0 ].revents | pollfds[ 1 ].revents) & (POLLERR | POLLHUP | POLLNVAL) ) { break; diff --git a/terminal.cpp b/terminal.cpp index 711b4b3..3851ce2 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -34,7 +34,7 @@ void Emulator::print( Parser::Print *act ) Cell *this_cell = fb.get_cell(); - Cell *combining_cell = fb.get_combining_cell(); + Cell *combining_cell = fb.get_combining_cell(); /* can be null if we were resized */ switch ( chwidth ) { case 1: /* normal character */ @@ -68,6 +68,11 @@ void Emulator::print( Parser::Print *act ) act->handled = true; break; case 0: /* combining character */ + if ( combining_cell == NULL ) { /* character is now offscreen */ + act->handled = true; + break; + } + if ( combining_cell->contents.size() == 0 ) { /* cell starts with combining character */ assert( this_cell == combining_cell ); @@ -195,3 +200,8 @@ std::string Emulator::close( void ) char ansimode[ 6 ] = { 0x1b, '[', '?', '1', 'l', 0 }; return std::string( ansimode ); } + +void Emulator::resize( size_t s_width, size_t s_height ) +{ + fb.resize( s_width, s_height ); +} diff --git a/terminal.hpp b/terminal.hpp index fa838df..83e764e 100644 --- a/terminal.hpp +++ b/terminal.hpp @@ -46,6 +46,9 @@ namespace Terminal { std::string open( void ); /* put user cursor keys in application mode */ std::string close( void ); /* restore user cursor keys */ + + void resize( size_t s_width, size_t s_height ); + }; } diff --git a/terminalframebuffer.cpp b/terminalframebuffer.cpp index 17f1a07..1ca9a90 100644 --- a/terminalframebuffer.cpp +++ b/terminalframebuffer.cpp @@ -56,7 +56,10 @@ DrawState::DrawState( int s_width, int s_height ) Framebuffer::Framebuffer( int s_width, int s_height ) : rows( s_height, Row( s_width ) ), window_title(), ds( s_width, s_height ) -{} +{ + assert( s_height > 0 ); + assert( s_width > 0 ); +} void Framebuffer::scroll( int N ) { @@ -183,6 +186,11 @@ Cell *Framebuffer::get_cell( int row, int col ) Cell *Framebuffer::get_combining_cell( void ) { + if ( (ds.get_combining_char_row() >= ds.get_width()) + || (ds.get_combining_char_col() >= ds.get_height()) ) { + return NULL; + } /* can happen if a resize came in between */ + return &rows[ ds.get_combining_char_row() ].cells[ ds.get_combining_char_col() ]; } @@ -280,7 +288,7 @@ void DrawState::restore_cursor( void ) auto_wrap_mode = save.auto_wrap_mode; origin_mode = save.origin_mode; - snap_cursor_to_border(); + snap_cursor_to_border(); /* we could have resized in between */ new_grapheme(); } @@ -352,3 +360,40 @@ void Framebuffer::soft_reset( void ) ds.clear_renditions(); ds.clear_saved_cursor(); } + +void Framebuffer::resize( int s_width, int s_height ) +{ + assert( s_width > 0 ); + assert( s_height > 0 ); + + rows.resize( s_height, Row( ds.get_width() ) ); + + for ( std::deque::iterator i = rows.begin(); + i != rows.end(); + i++ ) { + (*i).cells.resize( s_width, Cell() ); + } + + ds.resize( s_width, s_height ); +} + +void DrawState::resize( int s_width, int s_height ) +{ + width = s_width; + height = s_height; + + snap_cursor_to_border(); + + if ( scrolling_region_top_row >= height ) { + scrolling_region_top_row = 0; + } + + if ( scrolling_region_bottom_row >= height ) { + scrolling_region_bottom_row = height - 1; + } + + tabs.resize( width ); + + /* saved cursor will be snapped to border on restore */ + /* combining char cell can now be offscreen */ +} diff --git a/terminalframebuffer.hpp b/terminalframebuffer.hpp index 99509a1..aca8bcb 100644 --- a/terminalframebuffer.hpp +++ b/terminalframebuffer.hpp @@ -106,6 +106,8 @@ namespace Terminal { void restore_cursor( void ); void clear_saved_cursor( void ) { save = SavedCursor(); } + void resize( int s_width, int s_height ); + DrawState( int s_width, int s_height ); }; @@ -139,6 +141,8 @@ namespace Terminal { void set_window_title( std::vector s ) { window_title = s; } std::vector get_window_title( void ) { return window_title; } + + void resize( int s_width, int s_height ); }; }