如何编写ANSI C控制台屏幕缓冲区?

我正在制作一个基于ASCII的游戏,而且我看起来人们都说要使用来自MSDN的Console.Write(),如果你使用的是Windows,那就是花花公子,但我不是。

因此,我正在尝试在C中编写一个函数或一组函数,它们可以在两个屏幕缓冲区之间交替,并将它们写入屏幕,类似于手册页,以及pico,vim和emacs的。

我有缓冲区正在工作,并发现一个名为0verkill的旧的ASCII游戏,它使用C和putchar()将每个字符放在屏幕上,但我尝试重新创建它,导致文本连续流动,而不是窗口大小的静态文本面板。 我真的不想使用像curses这样的任何外部库(因为这会降低可移植性),并且如果可能的话,我希望保持ansi标准。

谢谢!

我真的不想使用像curses这样的任何外部库(因为这会降低可移植性)

什么? 像curses和ncurses这样的库旨在使这种东西便携,因为……

并且如果可能的话,我希望遵守ansi标准。

…对于你想要的东西,没有ANSI标准(至少对C来说)。 每个操作系统都以不同的方式实现这种行为,因此如果您希望以可移植的方式执行此操作,则需要使用库。 老实说,我讨厌必须开发一个没有移植到它的ncurses的系统。 想象一下没有它你将无法使用的所有程序。

我认为你要找的是ANSI控制字符ESC[2J ,它清除了屏幕。 您可以在状态更改后调用它来“刷新”控制台屏幕。

请参阅此页面以了解其余部分。 使用这些代码,您可以在控制台上定义颜色和格式(间距,对齐,缩进等)。

有一个ANSI标准X3.64,也有ISO / IEC 6429,它描述了DEC VT100终端。 该标准描述了兼容的终端仿真器将识别的颜色和光标定位的某些转义序列 ,它基本上是所有X终端,但在Windows上不一定(可能需要用户加载ansi.sys )。 这是最后一个丑陋的不一致,说明了为什么你应该使用ncurses来抽象出这些细节。

示例标题和源文件,说明从应用程序中抽象curses的方法。 收集灰尘; 15年前写的。 买者自负。

cursemu.h

 /*************************************************************************** * * DO NOT CHANGE ANYTHING BETWEEN THIS LINE AND THE NEXT LINE THAT HAS THE * WORDS "KLAATU BARRATA NIKTO" ON IT * ***************************************************************************/ #ifndef X__CURSEMU__H #define X__CURSEMU__H #include  #ifdef linux #define _POSIX_VERSION #endif #ifndef _POSIX_VERSION #include  #define USE_OLD_TTY #include  #undef USE_OLD_TTY #ifndef CBREAK #define CBREAK RAW #endif #if !defined(sun) && !defined(sequent) && !defined(hpux) && \ !defined(_AIX) && !defined(aix) #include  #define strchr index #else #include  #endif #else #include  #include  #endif #include  #include  #include  #include  #include  #include  #include  #include  #include  /* Keep looking ... */ int _tty_ch; #ifdef _POSIX_VERSION struct termios _tty; tcflag_t _res_iflg, _res_lflg; #define cbreak()(_tty.c_lflag&=~ICANON, \ tcsetattr( _tty_ch, TCSANOW, &_tty )) #define noecho()(_tty.c_lflag &= ~(ECHO|ICRNL), \ tcsetattr( _tty_ch, TCSADRAIN, &_tty )) #define savetty()((void) tcgetattr(_tty_ch, &_tty), \ _res_iflg = _tty.c_iflag, _res_lflg = _tty.c_lflag ) #define resetty()(_tty.c_iflag = _res_iflg, _tty.c_lflag = _res_lflg,\ (void) tcsetattr(_tty_ch, TCSADRAIN, &_tty)) #define erasechar()(_tty.c_cc[VERASE]) #else struct sgttyb _tty; int _res_flg; #define cbreak()(_tty.sg_flags|=CBREAK, ioctl(_tty_ch, TIOCSETP, &_tty)) #define noecho()(_tty.sg_flags &= ~(ECHO|CRMOD), \ ioctl( _tty_ch, TIOCSETP, &_tty )) #define savetty()((void) ioctl(_tty_ch, TIOCGETP, &_tty), \ _res_flg = _tty.sg_flags ) #define resetty()(_tty.sg_flags = _res_flg, \ (void) ioctl(_tty_ch, TIOCSETP, &_tty)) #define erasechar()(_tty.sg_erase) #endif /* KLAATU BARRATA NIKTO */ #define TERMCAP_LENGTH 1024 struct CtrlSeq { char termcap[ TERMCAP_LENGTH ]; int numRows, numCols; /* These pointers are indexes into the termcap buffer, and represent the * control sequences neccessary to send to the terminal window to perform * their appropriately named feature. */ char *highlight, *endMode, /* End highlight mode, and other modes. */ *clearScr, *clearEol, *scrollRegion, *moveCursor, *deleteRow, *insertRow, *saveCursor, /* Save the current cursor position */ *restoreCursor; /* Restore the saved cursor position */ int dumbTerm, /* 1 if the terminal is a dumb terminal */ flush; /* 1 if the emulation should flush stdout */ }; struct CtrlSeq ctrlSeq; #define DEFAULT_COLS 80 #define DEFAULT_ROWS 24 void ce_flush( int toSet ); void ce_puts( char *str ); void ce_gotoRowCol( int row, int col ); void ce_writeStrRowCol( char *theText, int row, int col ); void ce_writeStr( char *theText ); void ce_writeCharRowCol( char theChar, int row, int col ); void ce_writeChar( char theChar ); void ce_clearScreen( void ); void ce_clearEol( void ); void ce_highlight( int on ); void ce_scrollRegion( int row1, int row2 ); void ce_deleteRow( int row ); void ce_insertRow( int row ); void ce_saveCursor( void ); void ce_restoreCursor( void ); int ce_getRows( void ); int ce_getCols( void ); #endif 

cursemu.c

 #include "cursemu.h" int putchar_x( int c ) { return( putchar( c ) ); } /* Returns 0 on success, -1 on error */ int ce_startCurses( void ) { char *ptr, tempBuff[ 1024 ]; int result = 0; if( (ptr = (char *)getenv( "TERM" )) != NULL ) result = tgetent( tempBuff, ptr ); else result = tgetent( tempBuff, "vt100" ); if( result < 1 ) { perror( "FATAL Error: No termcap entry found (even tried vt100)!\n" ); return( -1 ); } ptr = ctrlSeq.termcap; if( (ctrlSeq.numCols = tgetnum( "co" )) == -1 ) ctrlSeq.numCols = DEFAULT_COLS; if( (ctrlSeq.numRows = tgetnum( "li" )) == -1 ) ctrlSeq.numRows = DEFAULT_ROWS; if( (ctrlSeq.moveCursor = (char *)tgetstr( "cm", &ptr )) == NULL ) ctrlSeq.moveCursor = (char *)tgetstr( "cl", &ptr ); if( (ctrlSeq.highlight = (char *)tgetstr( "mr", &ptr )) == NULL ) ctrlSeq.highlight = (char *)tgetstr( "md", &ptr ); ctrlSeq.endMode = (char *)tgetstr( "me", &ptr ); ctrlSeq.clearEol = (char *)tgetstr( "ce", &ptr ); ctrlSeq.clearScr = (char *)tgetstr( "cl", &ptr ); ctrlSeq.scrollRegion = (char *)tgetstr( "cs", &ptr ); ctrlSeq.deleteRow = (char *)tgetstr( "dl", &ptr ); ctrlSeq.insertRow = (char *)tgetstr( "al", &ptr ); ctrlSeq.saveCursor = (char *)tgetstr( "sc", &ptr ); ctrlSeq.restoreCursor = (char *)tgetstr( "rc", &ptr ); ctrlSeq.dumbTerm = (ctrlSeq.moveCursor == NULL) || (ctrlSeq.scrollRegion == NULL) || (ctrlSeq.saveCursor == NULL) || (ctrlSeq.restoreCursor == NULL) || (ctrlSeq.clearEol == NULL); ctrlSeq.flush = 1; if( !ctrlSeq.dumbTerm ) { if( (_tty_ch = open( "/dev/tty", O_RDWR, 0 ) ) == -1 ) _tty_ch = 0; savetty(); cbreak(); noecho(); return( 0 ); } return( -1 ); } int ce_endCurses( void ) { ce_scrollRegion( -1, -1 ); ce_gotoRowCol( ce_getRows() - 1, 0 ); resetty(); } void ce_flush( int toSet ) { ctrlSeq.flush = toSet; if( toSet == 1 ) fflush( stdout ); } void ce_puts( char *str ) { tputs( str, 0, putchar_x ); if( ctrlSeq.flush ) fflush( stdout ); } void ce_gotoRowCol( int row, int col ) { if( row > ctrlSeq.numRows ) row = ctrlSeq.numRows; if( col > ctrlSeq.numCols ) col = ctrlSeq.numCols; ce_puts( (char *)tgoto( ctrlSeq.moveCursor, col, row ) ); } void ce_writeStrRowCol( char *theText, int row, int col ) { ce_flush( 0 ); ce_gotoRowCol( row, col ); ce_writeStr( theText ); ce_flush( 1 ); } void ce_writeStr( char *theText ) { ce_flush( 0 ); printf( "%s", theText ); ce_flush( 1 ); } void ce_writeCharRowCol( char theChar, int row, int col ) { ce_flush( 0 ); ce_gotoRowCol( row, col ); ce_writeChar( theChar ); ce_flush( 1 ); } void ce_writeChar( char theChar ) { ce_flush( 0 ); printf( "%c", theChar ); ce_flush( 1 ); } void ce_clearScreen( void ) { ce_puts( ctrlSeq.clearScr ); } void ce_clearEol( void ) { ce_puts( ctrlSeq.clearEol ); } void ce_highlight( int on ) { if( on == 0 ) ce_puts( ctrlSeq.endMode ); else ce_puts( ctrlSeq.highlight ); } void ce_scrollRegion( int row1, int row2 ) { ce_puts( (char *)tgoto( ctrlSeq.scrollRegion, row1, row2 ) ); } void ce_deleteRow( int row ) { ce_gotoRowCol( row, 0 ); ce_puts( ctrlSeq.deleteRow ); } void ce_insertRow( int row ) { ce_gotoRowCol( row, 0 ); ce_puts( ctrlSeq.insertRow ); } void ce_saveCursor( void ) { ce_puts( ctrlSeq.saveCursor ); } void ce_restoreCursor( void ) { ce_puts( ctrlSeq.restoreCursor ); } int ce_getRows( void ) { return( ctrlSeq.numRows ); } int ce_getCols( void ) { return( ctrlSeq.numCols ); } 

编译

要求:

 gcc -o cursemu.o -lcurses -ltermcap