/////////////////////////////////////////////////////////////////////////////// //Telnet Win32 : an ANSI telnet client. //Copyright (C) 1998-2000 Paul Brannan //Copyright (C) 1998 I.Ioannou //Copyright (C) 1997 Brad Johnson // //This program is free software; you can redistribute it and/or //modify it under the terms of the GNU General Public License //as published by the Free Software Foundation; either version 2 //of the License, or (at your option) any later version. // //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. // //You should have received a copy of the GNU General Public License //along with this program; if not, write to the Free Software //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // //I.Ioannou //roryt@hol.gr // /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // Module: tscroll.cpp // // Contents: Telnet Handler // // Product: telnet // // Revisions: Dec. 5, 1998 Paul Brannan // June 15, 1998 Paul Brannan // // This is code originally from tnclass.cpp and ansiprsr.cpp // /////////////////////////////////////////////////////////////////////////////// #include "precomp.h" enum { HEX, DUMP, DUMPB, TEXTB, }; int DummyStripBuffer(char *start, char *end, int width) {return 0;} TScroller::TScroller(TMouse &M, int size) : Mouse(M) { iScrollSize = size; pcScrollData = new char[iScrollSize]; iScrollEnd = 0; iPastEnd = 0; memset(pcScrollData, ' ', iScrollSize); if(stricmp(ini.get_scroll_mode(), "hex") == 0) iDisplay = HEX; else if(stricmp(ini.get_scroll_mode(), "dump") == 0) iDisplay = DUMP; else if(stricmp(ini.get_scroll_mode(), "dumpb") == 0) iDisplay = DUMPB; else if(stricmp(ini.get_scroll_mode(), "text") == 0) iDisplay = TEXTB; else iDisplay = DUMP; strip = &DummyStripBuffer; } TScroller::~TScroller() { delete[] pcScrollData; } void TScroller::init(stripfunc *s) { strip = s; } // Fixed update of circular buffer (Paul Brannan 12/4/98) // Note: iScrollEnd is one character beyond the end void TScroller::update(const char *pszHead, const char *pszTail) { if ((iScrollEnd)+(pszTail-pszHead) < iScrollSize) { memcpy(&pcScrollData[iScrollEnd], pszHead, pszTail-pszHead); } else if (pszTail-pszHead > iScrollSize) { memcpy(pcScrollData, pszTail-iScrollSize, iScrollSize); iScrollEnd = 0; } else { memcpy(&pcScrollData[iScrollEnd], pszHead, iScrollSize-iScrollEnd); memcpy(&pcScrollData[0], pszHead + (iScrollSize-iScrollEnd), pszTail-pszHead-(iScrollSize-iScrollEnd)); } // This could probably be optimized better, but it's probably not worth it int temp = iScrollEnd; iScrollEnd = ((iScrollEnd)+(pszTail-pszHead))%iScrollSize; if(iScrollEnd < temp) iPastEnd = 1; } // Perhaps this should be moved to Tconsole.cpp? (Paul Brannan 6/12/98) static BOOL WriteConsoleOutputCharAndAttribute( HANDLE hConsoleOutput, // handle of a console screen buffer CHAR * lpWriteBuffer, WORD wAttrib, SHORT sX, SHORT sY ){ // we ought to allocate memory before writing to an address (PB 5/12/98) DWORD cWritten; const LPDWORD lpcWritten = &cWritten; DWORD cWriteCells = strlen(lpWriteBuffer); COORD coordWrite = {sX,sY}; LPWORD lpwAttribute = new WORD[cWriteCells]; for (unsigned int i = 0; i < cWriteCells; i++) lpwAttribute[i] = wAttrib; WriteConsoleOutputAttribute( hConsoleOutput, // handle of a console screen buffer lpwAttribute, // address of buffer to write attributes from cWriteCells, // number of character cells to write to coordWrite, // coordinates of first cell to write to lpcWritten // address of number of cells written to ); WriteConsoleOutputCharacter( hConsoleOutput, // handle of a console screen buffer lpWriteBuffer, // address of buffer to write characters from cWriteCells, // number of character cells to write to coordWrite, // coordinates of first cell to write to lpcWritten // address of number of cells written to ); delete [] lpwAttribute; return 1; } static void hexify(int x, char *str, int len) { for(int j = len - 1; j >= 0; j--) { str[j] = x % 16; if(str[j] > 9) str[j] += 'A' - 10; else str[j] += '0'; x /= 16; } } static int setmaxlines(int iDisplay, int iScrollSize, int strippedlines, int con_width) { switch(iDisplay) { case HEX: return(iScrollSize / 16); break; case DUMP: case DUMPB: return(iScrollSize / con_width); break; case TEXTB: return(strippedlines); break; } return 0; } static void setstatusline(char *szStatusLine, int len, int iDisplay) { memset(szStatusLine, ' ', len); memcpy(&szStatusLine[1], "Scrollback Mode", 15); switch(iDisplay) { case HEX: memcpy(&szStatusLine[len / 2 - 1], "HEX", 3); break; case DUMP: memcpy(&szStatusLine[len / 2 - 2], "DUMP", 4); break; case DUMPB: memcpy(&szStatusLine[len / 2 - 5], "BINARY DUMP", 11); break; case TEXTB: memcpy(&szStatusLine[len / 2 - 2], "TEXT", 4); break; } memcpy(&szStatusLine[len - 6], "READY", 5); szStatusLine[len] = 0; } void TScroller::ScrollBack(){ char p; int r,c; // define colors (Paul Brannan 7/5/98) int normal = (ini.get_scroll_bg() << 4) | ini.get_scroll_fg(); // int inverse = (ini.get_scroll_fg() << 4) | ini.get_scroll_bg(); int status = (ini.get_status_bg() << 4) | ini.get_status_fg(); CHAR_INFO* chiBuffer; chiBuffer = newBuffer(); saveScreen(chiBuffer); HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo; GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo); // Update iScrollBegin -- necessary in case the buffer isn't full yet long iScrollBegin, iScrollLast; if(iPastEnd == 0) { iScrollBegin = 0; iScrollLast = iScrollEnd - 1; } else { iScrollBegin = iScrollEnd; iScrollLast = iScrollSize - 1; } // Create buffer with ANSI codes stripped // Fixed this to work properly with a circular buffer (PB 12/4/98) char *stripped = new char[iScrollSize]; memcpy(stripped, pcScrollData + iScrollBegin, iScrollSize - iScrollBegin); if(iScrollBegin != 0) memcpy(stripped + (iScrollSize - iScrollBegin), pcScrollData, iScrollBegin - 1); int strippedlines = (*strip)(stripped, stripped + iScrollLast, CON_COLS); // Calculate the last line of the scroll buffer (Paul Brannan 12/4/98) int maxlines = setmaxlines(iDisplay, iScrollLast + 1, strippedlines, CON_COLS); // init scroll position int current = maxlines - CON_HEIGHT + 1; if(current < 0) current = 0; // paint border and info // paint last two lines black on white char * szStatusLine; szStatusLine = new char[CON_WIDTH+2]; setstatusline(szStatusLine, CON_COLS, iDisplay); WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, status, CON_LEFT, CON_BOTTOM); // loop while not done BOOL done = FALSE; while (!done){ switch (iDisplay){ case HEX: memset(szStatusLine, ' ', CON_COLS); szStatusLine[8] = ':'; szStatusLine[34] = '-'; for (r = 0; r < CON_HEIGHT; r++) { hexify((r + current) * 16, &szStatusLine[2], 6); for (c = 0; c < 16; c++){ if (c+(16*(r+current)) >= iScrollLast) p = 0; else p = pcScrollData[(c+16*(r+current) + iScrollBegin) % iScrollSize]; hexify((char)p, &szStatusLine[11 + 3*c], 2); if (!iscntrl(p)) { szStatusLine[60 + c] = (char)p; } else { szStatusLine[60 + c] = '.'; } } for(int j = 0; j < 16; j++) { } szStatusLine[CON_COLS] = '\0'; WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, normal, CON_LEFT, r+CON_TOP); } break; case DUMP: for (r = 0; r < CON_HEIGHT; r++) { for (c = 0; c <= CON_WIDTH; c++) { if (c+((CON_COLS)*(r+current)) >= iScrollLast) p = ' '; else p = pcScrollData[(c+((CON_COLS)*(r+current)) + iScrollBegin) % iScrollSize]; if (!iscntrl(p)) szStatusLine[c] = p; else szStatusLine[c] = '.'; } szStatusLine[c] = '\0'; WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, normal, CON_LEFT, r+CON_TOP); } break; case DUMPB: for (r = 0; r < CON_HEIGHT; r++) { for (c = 0; c <= CON_WIDTH; c++) { if (c+((CON_COLS)*(r+current)) >= iScrollLast) p = ' '; else p = pcScrollData[ (c+((CON_COLS)*(r+current)) + iScrollBegin) % iScrollSize]; if (p != 0) szStatusLine[c] = p; else szStatusLine[c] = ' '; } szStatusLine[c] = '\0'; WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, normal, CON_LEFT, r+CON_TOP); } break; case TEXTB: { int ch, lines, x; // Find the starting position for(ch = 0, lines = 0, x = 1; ch < iScrollSize && lines < current; ch++, x++) { if(stripped[ch] == '\n') lines++; if(stripped[ch] == '\r') x = 1; } for (r = 0; r < CON_HEIGHT; r++) { memset(szStatusLine, ' ', CON_COLS); for(c = 0; c <= CON_WIDTH; c++) { done = FALSE; if (ch >= iScrollSize) p = ' '; else p = stripped[ch]; switch(p) { case 10: done = TRUE; break; case 13: c = 0; break; default: szStatusLine[c] = p; } ch++; if(done) break; } szStatusLine[CON_COLS] = '\0'; WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, normal, CON_LEFT, r+CON_TOP); } } break; } setstatusline(szStatusLine, CON_COLS, iDisplay); WriteConsoleOutputCharAndAttribute(hStdout, szStatusLine, status, CON_LEFT, CON_BOTTOM); // paint scroll back data // get key input switch(scrollkeys()){ case VK_ESCAPE: done = TRUE; break; case VK_PRIOR: if ( current > CON_HEIGHT) current-= CON_HEIGHT; else current = 0; break; case VK_NEXT: if ( current < maxlines - 2*CON_HEIGHT + 2) current += CON_HEIGHT; else current = maxlines - CON_HEIGHT + 1; break; case VK_DOWN: if (current <= maxlines - CON_HEIGHT) current++; break; case VK_UP: if ( current > 0) current--; break; case VK_TAB: iDisplay = (iDisplay+1)%4; maxlines = setmaxlines(iDisplay, iScrollLast + 1, strippedlines, CON_COLS); if(current > maxlines) current = maxlines - 1; if(current < 0) current = 0; break; case VK_END: current = maxlines - CON_HEIGHT + 1; if(current < 0) current = 0; break; case VK_HOME: current = 0; break; case SC_MOUSE: Mouse.scrollMouse(); break; } } // Clean up restoreScreen(chiBuffer); delete[] szStatusLine; delete[] chiBuffer; delete[] stripped; }