libyui-ncurses  2.44.1
/usr/src/RPM/BUILD/libyui-ncurses-2.44.1/src/NCurses.cc
00001 /*
00002   Copyright (C) 2000-2012 Novell, Inc
00003   This library is free software; you can redistribute it and/or modify
00004   it under the terms of the GNU Lesser General Public License as
00005   published by the Free Software Foundation; either version 2.1 of the
00006   License, or (at your option) version 3.0 of the License. This library
00007   is distributed in the hope that it will be useful, but WITHOUT ANY
00008   WARRANTY; without even the implied warranty of MERCHANTABILITY or
00009   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
00010   License for more details. You should have received a copy of the GNU
00011   Lesser General Public License along with this library; if not, write
00012   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
00013   Floor, Boston, MA 02110-1301 USA
00014 */
00015 
00016 
00017 /*-/
00018 
00019    File:       NCurses.cc
00020 
00021    Author:     Michael Andres <ma@suse.de>
00022 
00023 /-*/
00024 
00025 #include <unistd.h>
00026 #include <string.h>     // strcmp(), strerror()
00027 
00028 #include <cstdarg>
00029 #include <fstream>
00030 #include <list>
00031 #include <set>
00032 
00033 #include <yui/Libyui_config.h>
00034 
00035 #define  YUILogComponent "ncurses"
00036 #include <yui/YUILog.h>
00037 #include "NCurses.h"
00038 #include "NCDialog.h"
00039 #include "NCi18n.h"
00040 
00041 #include "stdutil.h"
00042 #include <signal.h>
00043 
00044 #include <sys/types.h>
00045 #include <sys/stat.h>
00046 #include <fcntl.h>
00047 #include <fnmatch.h>
00048 
00049 using stdutil::vform;
00050 using stdutil::form;
00051 
00052 /*
00053   Textdomain "ncurses"
00054  */
00055 
00056 NCurses * NCurses::myself = 0;
00057 std::set<NCDialog*> NCurses::_knownDlgs;
00058 const NCursesEvent NCursesEvent::Activated( NCursesEvent::button, YEvent::Activated );
00059 const NCursesEvent NCursesEvent::SelectionChanged( NCursesEvent::button, YEvent::SelectionChanged );
00060 const NCursesEvent NCursesEvent::ValueChanged( NCursesEvent::button, YEvent::ValueChanged );
00061 
00062 
00063 
00064 
00065 #define CONVERR(n,p) \
00066     va_list ap;        \
00067     va_list ap1;       \
00068     va_start( ap, p ); \
00069     va_start( ap1, p );\
00070     errval_i = n;      \
00071     errmsg_t = vform( p, ap, ap1 ); \
00072     va_end( ap );      \
00073     va_end( ap1 )
00074 
00075 NCursesError::NCursesError( const char * msg, ... )
00076         : errval_i( ERR )
00077 {
00078     CONVERR( ERR, msg );
00079 }
00080 
00081 NCursesError::NCursesError( int val, const char * msg, ... )
00082         : errval_i( val )
00083 {
00084     CONVERR( val, msg );
00085 }
00086 
00087 NCursesError & NCursesError::NCError( const char * msg, ... )
00088 {
00089     CONVERR( ERR, msg );
00090     return *this;
00091 }
00092 
00093 NCursesError & NCursesError::NCError( int val, const char * msg, ... )
00094 {
00095     CONVERR( val, msg );
00096     return *this;
00097 }
00098 
00099 #undef CONVERR
00100 
00101 
00102 std::ostream & operator<<( std::ostream & STREAM, const NCursesError & OBJ )
00103 {
00104     STREAM << form( "%s: (%d) %s"
00105                     , OBJ.location()
00106                     , OBJ.errval_i
00107                     , OBJ.errmsg_t.c_str() );
00108     return STREAM;
00109 }
00110 
00111 
00112 std::ostream & operator<<( std::ostream & STREAM, const NCursesEvent & OBJ )
00113 {
00114 #define ENUM_OUT(v) case NCursesEvent::v: return STREAM << "Ev::" << #v
00115 
00116     switch ( OBJ.type )
00117     {
00118         ENUM_OUT( none );
00119         ENUM_OUT( handled );
00120         ENUM_OUT( cancel );
00121         ENUM_OUT( button );
00122         ENUM_OUT( menu );
00123         ENUM_OUT( timeout );
00124         ENUM_OUT( key );
00125     }
00126 
00127 #undef ENUM_OUT
00128     return STREAM << "Ev::unknown";
00129 }
00130 
00131 
00132 
00133 NCurses::NCurses()
00134         : theTerm( 0 )
00135         , title_w( 0 )
00136         , status_w( 0 )
00137         , styleset( 0 )
00138         , stdpan( 0 )
00139 {
00140     const char * term = getenv( "TERM" );
00141 
00142     if ( term && *term )
00143         envTerm = term;
00144 }
00145 
00146 
00147 
00148 NCurses::~NCurses()
00149 {
00150     yuiMilestone() << "Shutdown NCurses..." << std::endl;
00151     myself = 0;
00152 
00153     //restore env. variable - might have been changed by NCurses::init()
00154     setenv( "TERM", envTerm.c_str(), 1 );
00155     delete styleset;
00156     delete stdpan;
00157 
00158     if ( title_w )
00159         ::delwin( title_w );
00160 
00161     if ( status_w )
00162         ::delwin( status_w );
00163 
00164     ::endwin();
00165 
00166     if ( theTerm )
00167         ::delscreen( theTerm );
00168 
00169     yuiMilestone() << "NCurses down" << std::endl;
00170 }
00171 
00172 
00173 
00174 WINDOW * NCurses::ripped_w_top = 0;
00175 WINDOW * NCurses::ripped_w_bottom = 0;
00176 
00177 int NCurses::ripinit_top( WINDOW * w, int c )
00178 {
00179     ripped_w_top = w;
00180     return OK;
00181 }
00182 
00183 
00184 int NCurses::ripinit_bottom( WINDOW * w, int c )
00185 {
00186     ripped_w_bottom = w;
00187     return OK;
00188 }
00189 
00190 
00191 void NCurses::init()
00192 {
00193     yuiMilestone() << "Launch NCurses..."
00194 #ifdef VERSION
00195     << "(ui-ncurses-" << VERSION << ")"
00196 #endif
00197     << std::endl;
00198     yuiMilestone() << "TERM=" << envTerm << std::endl;
00199 
00200     signal( SIGINT, SIG_IGN );  // ignore Ctrl C
00201 
00202     //rip off the top line
00203 
00204     if ( title_line() && ::ripoffline( 1, ripinit_top ) != OK )
00205         throw NCursesError( "ripoffline() failed" );
00206 
00207     //and bottom line (-1 means 1st from the bottom)
00208     if ( ::ripoffline( -1, ripinit_bottom ) != OK )
00209         throw NCursesError( "ripoffline() failed" );
00210 
00211     yuiMilestone() << "isatty(stdin)" << ( isatty( 0 ) ? "yes" : "no" ) << std::endl;
00212 
00213     if ( isatty( 0 ) )
00214     {
00215         char * mytty = ttyname( 0 );
00216 
00217         if ( mytty )
00218         {
00219             yuiMilestone() << "mytty: " << mytty << std::endl;
00220             FILE * fdi = fopen( mytty, "r" );
00221 
00222             if ( !fdi )
00223             {
00224                 yuiError() << "fdi: (" << errno << ") " << strerror( errno ) << std::endl;
00225             }
00226 
00227             FILE * fdo = fopen( mytty, "w" );
00228 
00229             if ( !fdo )
00230             {
00231                 yuiError() << "fdo: (" << errno << ") " << strerror( errno ) << std::endl;
00232             }
00233 
00234             if ( fdi && fdo )
00235             {
00236                 theTerm = newterm( 0, fdo, fdi );
00237 
00238                 //initialization failed
00239 
00240                 if ( theTerm == NULL )
00241                 {
00242                     //bug #235954: workaround missing terminfos in inst-sys
00243                     //let's close the first term
00244                     ::endwin();
00245 
00246                     std::string fallbackTerm = "";
00247                     //try generic xterm for xterm-like terminals, otherwise use vt100
00248 
00249                     if ( ! fnmatch( "xterm*", envTerm.c_str(), 0 ) )
00250                         fallbackTerm = "xterm";
00251                     else
00252                         fallbackTerm = "vt100";
00253 
00254                     yuiWarning() << "newterm() failed, using generic " << fallbackTerm << " as a fallback" << std::endl;
00255 
00256                     //overwrite environment variable
00257                     setenv( "TERM", fallbackTerm.c_str(), 1 );
00258 
00259                     //.. and try again
00260                     theTerm = newterm( 0, fdo, fdi );
00261 
00262                     if ( theTerm == NULL )
00263                         throw NCursesError( "fallback newterm() failed" );
00264                 }
00265 
00266                 if ( set_term( theTerm ) == NULL )
00267                     throw NCursesError( "set_term() failed" );
00268 
00269                 myTerm = mytty;
00270             }
00271         }
00272     }
00273 
00274     //duplicate stdout and stderr before redirecting them to log
00275     //so that they can be regenerated before system() call
00276     stdout_save = dup( 1 );
00277     stderr_save = dup( 2 );
00278 
00279     RedirectToLog();
00280 
00281     if ( !theTerm )
00282     {
00283         yuiMilestone() << "no term so fall back to initscr" << std::endl;
00284 
00285         if ( ::initscr() == NULL )
00286             throw NCursesError( "initscr() failed" );
00287     }
00288 
00289     yuiMilestone() << "have color = " << ::has_colors()  << std::endl;
00290 
00291     if ( want_colors() && ::has_colors() )
00292     {
00293         if ( ::start_color() != OK )
00294             throw NCursesError( "start_color() failed" );
00295 
00296         NCattribute::init_colors();
00297     }
00298 
00299     if ( title_line() )
00300     {
00301         if ( !ripped_w_top )
00302             throw NCursesError( "ripinit_top() failed" );
00303 
00304         title_w = ripped_w_top;
00305     }
00306 
00307     if ( !ripped_w_bottom )
00308         throw NCursesError( "ripinit_bottom() failed" );
00309 
00310     status_w = ripped_w_bottom;
00311 
00312     setup_screen();
00313 
00314     yuiMilestone() << form( "screen size %d x %d\n", lines(), cols() );
00315 
00316     myself = this;
00317     styleset = new NCstyle( envTerm );
00318     stdpan   = new NCursesPanel();
00319     stdpan->bkgd( style()( NCstyle::AppText ) );
00320 
00321     if ( title_line() )
00322         init_title();
00323     SetStatusLine( myself->status_line );
00324 
00325     init_screen();
00326     yuiMilestone() << "NCurses ready" << std::endl;
00327 }
00328 
00329 
00330 
00331 void NCurses::setup_screen()
00332 {
00333     ::cbreak();
00334     ::noecho();
00335     ::keypad( ::stdscr, true );
00336     ::meta( ::stdscr, true );
00337     ::leaveok( ::stdscr, true );
00338     ::curs_set( 0 );
00339 
00340     ::define_key( "\e[Z",   KEY_BTAB );
00341     ::define_key( "\e\t",   KEY_BTAB );
00342     ::define_key( "\030\t", KEY_BTAB );
00343 }
00344 
00345 
00346 
00347 void NCurses::init_title()
00348 {
00349     ::wbkgd( title_w, style()( NCstyle::AppTitle ) );
00350     ::wnoutrefresh( title_w );
00351     ::wbkgd( status_w, style()( NCstyle::AppTitle ) );
00352     ::wnoutrefresh( status_w );
00353 }
00354 
00355 
00356 
00357 void NCurses::init_screen()
00358 {
00359     bool redefine = false;
00360 
00361     char *value = getenv( "Y2NCPSEUDO" );
00362 
00363     // The 9.0 workaround for missing ACS chars (bug #30512) is not necessary
00364     // any longer (a patch is provided for ncurses-5.4).
00365 
00366     // Redefine ACS chars if Y2NCPSEUDO is set to "1" (just in case of ...)
00367 
00368     if ( value != NULL && strcmp( value, "1" ) == 0 )
00369     {
00370         redefine = true;
00371     }
00372 
00373     if ( redefine )
00374     {
00375         chtype cch = 0;
00376 
00377         NCattribute::setChar( cch, '+' );
00378         ACS_ULCORNER = cch;
00379         ACS_LLCORNER = cch;
00380         ACS_URCORNER = cch;
00381         ACS_LRCORNER = cch;
00382         ACS_LTEE = cch;
00383         ACS_RTEE = cch;
00384         ACS_BTEE = cch;
00385         ACS_TTEE = cch;
00386         ACS_PLUS = cch;
00387 
00388         NCattribute::setChar( cch, '|' );
00389         ACS_VLINE = cch;
00390 
00391         NCattribute::setChar( cch, '-' );
00392         ACS_HLINE = cch;
00393 
00394         NCattribute::setChar( cch, '#' );
00395         ACS_DIAMOND = cch;
00396         ACS_CKBOARD = cch;
00397         ACS_BOARD = cch;
00398 
00399         NCattribute::setChar( cch, '<' );
00400         ACS_LARROW = cch;
00401 
00402         NCattribute::setChar( cch, '>' );
00403         ACS_RARROW = cch;
00404 
00405         NCattribute::setChar( cch, 'v' );
00406         ACS_DARROW = cch;
00407 
00408         NCattribute::setChar( cch, '^' );
00409         ACS_UARROW = cch;
00410     }
00411 }
00412 
00413 
00414 const NCstyle & NCurses::style()
00415 {
00416     return *myself->styleset;
00417 }
00418 
00419 
00420 void NCurses::Update()
00421 {
00422     if ( myself && myself->initialized() )
00423     {
00424         //myself->stdpan->refresh();
00425         myself->stdpan->redraw();
00426     }
00427 }
00428 
00429 
00430 void NCurses::Refresh()
00431 {
00432     if ( myself && myself->initialized() )
00433     {
00434         yuiMilestone() << "start refresh ..." << std::endl;
00435         SetTitle( myself->title_t );
00436         SetStatusLine( myself->status_line );
00437         ::clearok( ::stdscr, true );
00438         myself->stdpan->refresh();
00439         yuiMilestone() << "done refresh ..." << std::endl;
00440     }
00441 }
00442 
00443 
00444 void NCurses::Redraw()
00445 {
00446     if ( myself && myself->initialized() )
00447     {
00448         yuiMilestone() << "start redraw ..." << std::endl;
00449 
00450         // initialize all dialogs rewdraw
00451         PANEL * pan = ::panel_above( NULL );
00452 
00453         while ( pan )
00454         {
00455             NCDialog * dlg = NCursesUserPanel<NCDialog>::UserDataOf( *pan );
00456 
00457             if ( dlg )
00458             {
00459                 dlg->Recoded();
00460             }
00461 
00462             pan = ::panel_above( pan );
00463         }
00464 
00465         // TBD: initialize all dialogs rewdraw
00466         Refresh();
00467 
00468         yuiMilestone() << "done redraw ..." << std::endl;
00469     }
00470 }
00471 
00472 
00473 void NCurses::SetTitle( const std::string & str )
00474 {
00475     if ( myself && myself->title_w )
00476     {
00477         myself->title_t = str;
00478         ::wbkgd( myself->title_w, myself->style()( NCstyle::AppTitle ) );
00479         ::wclear( myself->title_w );
00480 
00481         yuiMilestone() << "Draw title called" << std::endl;
00482 
00483 #if 0
00484         setTextdomain( "ncurses" );
00485         // part of title (headline) of the textmode yast
00486         NCstring helpF1( _( "Press F1 for Help" ) );
00487         NCtext textF1( helpF1 );
00488 
00489         int s = myself->title_w->_maxx - textF1.Columns();
00490 
00491         if ( NCstring::terminalEncoding() != "UTF-8" )
00492         {
00493             std::string out;
00494             NCstring::RecodeFromWchar( helpF1.str(), NCstring::terminalEncoding(), &out );
00495             ::mvwaddstr( myself->title_w, 0, s, out.c_str() );
00496         }
00497         else
00498         {
00499             ::mvwaddwstr( myself->title_w, 0, s, ( wchar_t * )helpF1.str().c_str() );
00500         }
00501 
00502 #endif
00503 
00504         ::mvwaddstr( myself->title_w, 0, 1, myself->title_t.c_str() );
00505         ::wnoutrefresh( myself->title_w );
00506     }
00507 
00508 }
00509 
00510 void NCurses::SetStatusLine( std::map <int, std::string> fkeys )
00511 {
00512 
00513     if ( myself && myself->status_w )
00514     {
00515         myself->status_line = fkeys;
00516         ::wbkgd( myself->status_w, myself->style()( NCstyle::AppTitle ) );
00517         ::werase( myself->status_w );
00518 
00519         char key[10];
00520         char value[100];
00521 
00522         std::map<int, std::string>::iterator it;
00523 
00524         for ( it = fkeys.begin(); it != fkeys.end(); ++it )
00525         {
00526             sprintf( key, " F%d ", ( *it ).first );
00527             //reverse F-key to make it more visible
00528             ::wattron( myself->status_w, A_REVERSE );
00529             ::waddstr( myself->status_w, key );
00530             ::wattroff( myself->status_w, A_REVERSE );
00531 
00532             sprintf( value, "%s ", ( *it ).second.c_str() );
00533             ::waddstr( myself->status_w, value );
00534         }
00535 
00536         ::wnoutrefresh( myself->status_w );
00537     }
00538 }
00539 
00540 
00541 
00542 void NCurses::drawTitle()
00543 {
00544     if ( myself && myself->title_w )
00545     {
00546         SetTitle( myself->title_t );
00547     }
00548 }
00549 
00550 
00551 
00552 void NCurses::RememberDlg( NCDialog * dlg_r )
00553 {
00554     if ( dlg_r )
00555     {
00556         _knownDlgs.insert( dlg_r );
00557     }
00558 }
00559 
00560 
00561 
00562 void NCurses::ForgetDlg( NCDialog * dlg_r )
00563 {
00564     if ( dlg_r )
00565     {
00566         _knownDlgs.erase( dlg_r );
00567     }
00568 }
00569 
00570 
00571 
00572 void NCurses::RedirectToLog()
00573 {
00574     std::string log = "/dev/null";      // this used to be get_log_filename()
00575 
00576     yuiMilestone() << "isatty(stderr)" << ( isatty( 2 ) ? "yes" : "no" ) << std::endl;
00577 
00578     if ( isatty( 2 ) && theTerm )
00579     {
00580         // redirect stderr to log
00581         close( 2 );
00582         open( log.c_str(), O_APPEND | O_CREAT, 0666 );
00583     }
00584 
00585     yuiMilestone() << "isatty(stdout)" << ( isatty( 1 ) ? "yes" : "no" ) << std::endl;
00586 
00587     if ( isatty( 1 ) && theTerm )
00588     {
00589         // redirect stdout to log
00590         close( 1 );
00591         open( log.c_str(), O_APPEND | O_CREAT, 0666 );
00592     }
00593 }
00594 
00595 
00596 
00597 void NCurses::ResizeEvent()
00598 {
00599     if ( myself && myself->initialized() )
00600     {
00601         yuiMilestone() << "start resize to " << NCurses::lines() << 'x' << NCurses::cols() << "..." << std::endl;
00602 
00603         // remember stack of visible dialogs.
00604         // don't hide on the fly, as it will mess up stacking order.
00605         std::list<NCDialog*> dlgStack;
00606 
00607         for ( PANEL * pan = ::panel_above( NULL ); pan; pan = ::panel_above( pan ) )
00608         {
00609             NCDialog * dlg = NCursesUserPanel<NCDialog>::UserDataOf( *pan );
00610 
00611             if ( dlg )
00612             {
00613                 dlgStack.push_back( dlg );
00614             }
00615         }
00616 
00617         // hide all visible dialogs.
00618         for ( std::list<NCDialog*>::iterator it = dlgStack.begin(); it != dlgStack.end(); ++it )
00619         {
00620             ( *it )->getInvisible();
00621         }
00622 
00623         drawTitle();
00624         Update();
00625 
00626         // relayout all dialogs
00627 
00628         for ( std::set<NCDialog*>::iterator it = _knownDlgs.begin(); it != _knownDlgs.end(); ++it )
00629         {
00630             ( *it )->resizeEvent();
00631         }
00632 
00633         // recreate stack of visible dialogs
00634         for ( std::list<NCDialog*>::iterator it = dlgStack.begin(); it != dlgStack.end(); ++it )
00635         {
00636             ( *it )->getVisible();
00637         }
00638 
00639         Update();
00640 
00641         //FIXME: remove this once libncurses is upgraded to 20080105 patchlevel
00642         //after the resize, status line window needs to be moved to the new pos.
00643         ::mvwin( myself->status_w, NCurses::lines(), 0 );
00644         SetStatusLine( myself->status_line );
00645         //update the screen
00646         ::touchwin( myself->status_w );
00647         ::doupdate();
00648 
00649         yuiMilestone() << "done resize ..." << std::endl;
00650     }
00651 }
00652 
00653 
00654 
00655 void NCurses::ScreenShot( const std::string & name )
00656 {
00657     if ( !myself )
00658         return;
00659 
00660     //ofstream out( name.c_str(), ios::out|ios::app );
00661     std::ostream & out( yuiMilestone() );
00662 
00663     int curscrlines = myself->title_line() ? lines() + 1 : lines();
00664 
00665     for ( int l = 0; l < curscrlines; ++l )
00666     {
00667         for ( int c = 0; c < cols(); ++c )
00668         {
00669 
00670             chtype al = ::mvwinch( ::curscr, l, c ) & ( A_ALTCHARSET | A_CHARTEXT );
00671 
00672             if ( al & A_ALTCHARSET )
00673             {
00674                 if ( al == ACS_ULCORNER
00675                      || al == ACS_LLCORNER
00676                      || al == ACS_URCORNER
00677                      || al == ACS_LRCORNER
00678                      || al == ACS_LTEE
00679                      || al == ACS_RTEE
00680                      || al == ACS_BTEE
00681                      || al == ACS_TTEE
00682                      || al == ACS_PLUS )
00683                 {
00684                     out << '+';
00685                 }
00686                 else if ( al == ACS_HLINE )
00687                 {
00688                     out << '-';
00689                 }
00690                 else if ( al == ACS_VLINE )
00691                 {
00692                     out << '|';
00693                 }
00694                 else if ( al == ACS_DIAMOND
00695                           || al == ACS_CKBOARD
00696                           || al == ACS_BOARD )
00697                 {
00698                     out << '#';
00699                 }
00700                 else if ( al == ACS_LARROW )
00701                 {
00702                     out << '<';
00703                 }
00704                 else if ( al == ACS_RARROW )
00705                 {
00706                     out << '>';
00707                 }
00708                 else if ( al == ACS_DARROW )
00709                 {
00710                     out << 'v';
00711                 }
00712                 else if ( al == ACS_UARROW )
00713                 {
00714                     out << '^';
00715                 }
00716                 else
00717                 {
00718                     out << ( char )( al & A_CHARTEXT );
00719                 }
00720             }
00721             else
00722             {
00723                 out << ( char )( al & A_CHARTEXT );
00724             }
00725 
00726         }
00727 
00728         out << std::endl;
00729     }
00730 }
00731 
00732 
00733 std::ostream & operator<<( std::ostream & STREAM, const NCurses & OBJ )
00734 {
00735     STREAM << form( "NC - %d x %d - colors %d - pairs %d\n"
00736                     , OBJ.lines(), OBJ.cols()
00737                     , NCattribute::colors(), NCattribute::color_pairs() );
00738 
00739     WINDOW * cw = ::stdscr;
00740     STREAM << form( "NC - rootw %p", cw );
00741 
00742     if ( cw )
00743         STREAM << form( " - (%2hd,%2hd)%2hdx%2hd - {%p - (%2d,%2d)}\n"
00744                         , cw->_begy, cw->_begx
00745                         , cw->_maxy, cw->_maxx
00746                         , cw->_parent
00747                         , cw->_pary, cw->_parx
00748                       );
00749     else
00750         STREAM << std::endl;
00751 
00752     cw = OBJ.title_w;
00753 
00754     STREAM << form( "NC - title %p", cw );
00755 
00756     if ( cw )
00757         STREAM << form( " - (%2hd,%2hd)%2hdx%2hd - {%p - (%2d,%2d)}\n"
00758                         , cw->_begy, cw->_begx
00759                         , cw->_maxy, cw->_maxx
00760                         , cw->_parent
00761                         , cw->_pary, cw->_parx
00762                       );
00763     else
00764         STREAM << std::endl;
00765 
00766     return STREAM;
00767 }
 All Classes Functions Variables