1 ///////////////////////////////////////////////////////////////////////////////
2 //Telnet Win32 : an ANSI telnet client.
3 //Copyright (C) 1998-2000 Paul Brannan
4 //Copyright (C) 1998 I.Ioannou
5 //Copyright (C) 1997 Brad Johnson
6 //
7 //This program is free software; you can redistribute it and/or
8 //modify it under the terms of the GNU General Public License
9 //as published by the Free Software Foundation; either version 2
10 //of the License, or (at your option) any later version.
11 //
12 //This program is distributed in the hope that it will be useful,
13 //but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //GNU General Public License for more details.
16 //
17 //You should have received a copy of the GNU General Public License
18 //along with this program; if not, write to the Free Software
19 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 //I.Ioannou
22 //roryt@hol.gr
23 //
24 ///////////////////////////////////////////////////////////////////////////
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 //
28 // Module:		tconsole.cpp
29 //
30 // Contents:	screen functions
31 //
32 // Product:		telnet
33 //
34 //
35 // Revisions: Mar. 29, 2000 pbranna@clemson (Paul Brannan)
36 //            June 15, 1998 pbranna@clemson.edu
37 //            May 16, 1998	pbranna@clemson.edu
38 //            05. Sep.1997  roryt@hol.gr (I.Ioannou)
39 //            11.May,1997   roryt@hol.gr
40 //            06.April,1997 roryt@hol.gr
41 //            30.M�rz.1997  Titus_Boxberg@public.uni-hamburg.de
42 //		      5.Dec.1996    jbj@nounname.com
43 //            Version 2.0
44 //            02.Apr.1995	igor.milavec@uni-lj.si
45 //					  Original code
46 //
47 ///////////////////////////////////////////////////////////////////////////////
48 
49 #include "precomp.h"
50 
51 // argsused doesn't work on MSVC++
52 #ifdef __BORLANDC__
53 #pragma argsused
54 #endif
55 
56 TConsole::TConsole(HANDLE h) {
57 	hConsole = h;
58 
59 	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
60 
61 	// Start with correct colors
62 	int color_fg = ini.get_normal_fg();
63 	int color_bg = ini.get_normal_bg();
64 	if(color_fg == -1)
65 		color_fg = defaultfg = origfg = ConsoleInfo.wAttributes & 0xF;
66 	else
67 		defaultfg = origfg = color_fg;
68 	if(color_bg == -1)
69 		color_bg = defaultbg = origbg = (ConsoleInfo.wAttributes >> 4) & 0xF;
70 	else
71 		defaultbg = origbg = color_bg;
72 	wAttributes = color_fg | (color_bg << 4);
73 	reverse = blink = underline = false;
74 	SetConsoleTextAttribute(hConsole, wAttributes);
75 
76 	insert_mode = 0;
77 
78 	// Set the screen size
79 	SetWindowSize(ini.get_term_width(), ini.get_term_height());
80 
81 	iScrollStart = -1;
82 	iScrollEnd = -1;
83 }
84 
85 TConsole::~TConsole() {
86 	wAttributes = origfg | (origbg << 4);
87 	SetCursorPosition(0, CON_HEIGHT);
88 	SetConsoleTextAttribute(hConsole, wAttributes);
89 	WriteCtrlChar('\x0a');
90 }
91 
92 // Paul Brannan 8/2/98
93 void TConsole::SetWindowSize(int width, int height) {
94 	SMALL_RECT sr = {
95 		CON_LEFT,
96 		CON_TOP,
97 		(width == -1) ? CON_RIGHT : CON_LEFT + width - 1,
98 		(height == -1) ? CON_BOTTOM : CON_TOP + height - 1
99 	};
100 	ConsoleInfo.dwSize.X = width;
101 	if(ConsoleInfo.dwSize.Y < height) ConsoleInfo.dwSize.Y = height;
102 	SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize);
103 	SetConsoleWindowInfo(hConsole, TRUE, &sr);
104 	SetConsoleScreenBufferSize(hConsole, ConsoleInfo.dwSize);
105 	sync();
106 }
107 
108 // Paul Brannan 5/15/98
109 void TConsole::sync() {
110 	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
111 }
112 
113 void TConsole::HighVideo() {
114 	wAttributes = wAttributes | (unsigned char) 8;
115 }
116 
117 void TConsole::LowVideo() {
118 	wAttributes = wAttributes & (unsigned char) (0xff-8);
119 }
120 
121 void TConsole::Normal() {
122 	// I.Ioannou 11 May 1997
123 	// Color 7 is correct on some systems (for example Linux)
124 	// but not with others (for example SCO)
125 	// we must preserve the colors :
126 	// 06/04/98 thanks to Paul a .ini parameter from now on
127 
128 	BlinkOff();
129 	UnderlineOff();
130 	if(ini.get_preserve_colors()) {
131 		ReverseOff();
132 		LowVideo();
133 	} else {
134 		fg = defaultfg;
135 		bg = defaultbg;
136 		wAttributes = (unsigned char)fg | (bg << 4);
137 		reverse = false;
138 	}
139 }
140 
141 void TConsole::SetForeground(unsigned char wAttrib) {
142 	if(reverse) bg = wAttrib; else fg = wAttrib;
143 	wAttributes = (wAttributes & (unsigned char)0x88) |
144 		(unsigned char)fg | (bg << 4);
145 }
146 
147 void TConsole::SetBackground(unsigned char wAttrib) {
148 	if(reverse) fg = wAttrib; else bg = wAttrib;
149 	wAttributes = (wAttributes & (unsigned char)0x88) |
150 		(unsigned char)fg | (bg << 4);
151 }
152 
153 // As far as I can tell, there's no such thing as blink in Windows Console.
154 // I tried using some inline asm to turn off high-intensity backgrounds,
155 // but I got a BSOD.  Perhaps there is an undocumented function?
156 // (Paul Brannan 6/27/98)
157 void TConsole::BlinkOn() {
158 	blink = 1;
159 	if(underline) {
160 		UlBlinkOn();
161 	} else {
162 		if(ini.get_blink_bg() != -1) {
163 			wAttributes &= 0x8f;					// turn off bg
164 			wAttributes |= ini.get_blink_bg() << 4;
165 		}
166 		if(ini.get_blink_fg() != -1) {
167 			wAttributes &= 0xf8;					// turn off fg
168 			wAttributes |= ini.get_blink_fg();
169 		}
170 		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
171 			wAttributes |= 0x80;
172 	}
173 }
174 
175 // Added by I.Ioannou 06 April, 1997
176 void TConsole::BlinkOff() {
177 	blink = 0;
178 	if(underline) {
179 		UlBlinkOff();
180 	} else {
181 		if(ini.get_blink_bg() != -1) {
182 			wAttributes &= 0x8f;					// turn off bg
183 			wAttributes |= defaultbg << 4;
184 		}
185 		if(ini.get_blink_fg() != -1) {
186 			wAttributes &= 0xf8;					// turn off fg
187 			wAttributes |= defaultfg;
188 		}
189 		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
190 			wAttributes &= 0x7f;
191 	}
192 }
193 
194 // Paul Brannan 6/27/98
195 void TConsole::UnderlineOn() {
196 	underline = 1;
197 	if(blink) {
198 		UlBlinkOn();
199 	} else {
200 		if(ini.get_underline_bg() != -1) {
201 			wAttributes &= 0x8f;					// turn off bg
202 			wAttributes |= ini.get_underline_bg() << 4;
203 		}
204 		if(ini.get_underline_fg() != -1) {
205 			wAttributes &= 0xf8;					// turn off fg
206 			wAttributes |= ini.get_underline_fg();
207 		}
208 		if(ini.get_underline_bg() == -1 && ini.get_underline_fg() == -1)
209 			wAttributes |= 0x80;
210 	}
211 }
212 
213 // Paul Brannan 6/27/98
214 void TConsole::UnderlineOff() {
215 	underline = 0;
216 	if(blink) {
217 		UlBlinkOff();
218 	} else {
219 		if(ini.get_blink_bg() != -1) {
220 			wAttributes &= 0x8f;					// turn off bg
221 			wAttributes |= defaultbg << 4;
222 		}
223 		if(ini.get_blink_fg() != -1) {
224 			wAttributes &= 0xf8;					// turn off fg
225 			wAttributes |= defaultfg;
226 		}
227 		if(ini.get_blink_bg() == -1 && ini.get_blink_fg() == -1)
228 			wAttributes &= 0x7f;
229 	}
230 }
231 
232 // Paul Brannan 6/27/98
233 void TConsole::UlBlinkOn() {
234 	if(ini.get_ulblink_bg() != -1) {
235 		wAttributes &= 0x8f;					// turn off bg
236 		wAttributes |= ini.get_ulblink_bg() << 4;
237 	}
238 	if(ini.get_ulblink_fg() != -1) {
239 		wAttributes &= 0xf8;					// turn off fg
240 		wAttributes |= ini.get_ulblink_fg();
241 	}
242 	if(ini.get_ulblink_bg() == -1 && ini.get_ulblink_fg() == -1)
243 		wAttributes |= 0x80;
244 }
245 
246 // Paul Brannan 6/27/98
247 void TConsole::UlBlinkOff() {
248 	if(blink) {
249 		BlinkOn();
250 	} else if(underline) {
251 		UnderlineOn();
252 	} else {
253 		Normal();
254 	}
255 }
256 
257 // Paul Brannan 6/26/98
258 void TConsole::Lightbg() {
259 	WORD *pAttributes = new WORD[CON_COLS];
260 	DWORD Result;
261 
262 	// Paul Brannan 8/5/98
263 	// Correction: processing more than one line at a time causes a segfault
264 	// if the screen width != 80
265 	for(int i = CON_TOP; i <= CON_BOTTOM; i++) {
266 		COORD Coord = {CON_LEFT, i};
267 
268 		ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS),
269 			Coord, &Result);
270 
271 		for(DWORD j = 0; j < Result; j++) pAttributes[j] |= 0x80;
272 
273 		WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord,
274 			&Result);
275 	}
276 
277 	delete[] pAttributes; // clean up
278 
279 	wAttributes |= (unsigned char)0x80;
280 	bg |= 8;
281 }
282 
283 // Paul Brannan 6/26/98
284 void TConsole::Darkbg() {
285 	WORD *pAttributes = new WORD[CON_COLS];
286 	DWORD Result;
287 
288 	// Paul Brannan 8/5/98
289 	// Correction: processing more than one line at a time causes a segfault
290 	// if the screen width != 80
291 	for(int i = CON_TOP; i <= CON_BOTTOM; i++) {
292 		COORD Coord = {CON_LEFT, i};
293 
294 		ReadConsoleOutputAttribute(hConsole, pAttributes, (DWORD)(CON_COLS),
295 			Coord, &Result);
296 
297 		for(DWORD j = 0; j < Result; j++) pAttributes[j] &= 0x7f;
298 
299 		WriteConsoleOutputAttribute(hConsole, pAttributes, Result, Coord,
300 			&Result);
301 	}
302 
303 	delete[] pAttributes; // clean up
304 
305 
306 	wAttributes &= (unsigned char)0x7f;
307 	bg &= 7;
308 }
309 
310 // Added by I.Ioannou 11.May,1997
311 void TConsole::ReverseOn() {
312 	if (!reverse) {
313 		reverse = true;
314 
315 		// atl  : forground attributes without the intensity
316 		// ath  : backgound attributes without the blink
317 		// bl   : the blink state
318 		// ints : the intensity
319 		unsigned char atl   = wAttributes & (unsigned char) 0x07;
320 		unsigned char ath   = wAttributes & (unsigned char) 0x70;
321 		unsigned char bl    = wAttributes & (unsigned char) 0x80;
322 		unsigned char ints  = wAttributes & (unsigned char) 0x08;
323 		wAttributes = bl | (atl << 4) | ints | (ath >> 4);
324 	}
325 }
326 
327 // Added by I.Ioannou 11.May,1997
328 void TConsole::ReverseOff() {
329 	if (reverse) {
330 		reverse = false;
331 		wAttributes = fg | (bg << 4);
332 	}
333 }
334 
335 unsigned long TConsole::WriteText(const char *pszString, unsigned long cbString) {
336 	DWORD Result;
337 
338 	if(insert_mode) {
339 		InsertCharacter(cbString);
340 	}
341 
342 	WriteConsoleOutputCharacter(hConsole, (char *)pszString, cbString,
343 		ConsoleInfo.dwCursorPosition, &Result);
344 	FillConsoleOutputAttribute(hConsole, wAttributes, cbString,
345 		ConsoleInfo.dwCursorPosition, &Result);
346 	return Result;
347 }
348 
349 // Formerly ConWriteString (Paul Brannan 6/28/98)
350 unsigned long TConsole::WriteStringFast(const char* pszString, unsigned long cbString) {
351 	DWORD Result;
352 
353 	SetConsoleTextAttribute(hConsole, wAttributes);
354 
355 	//check to see if the line is longer than the display
356 	if (!getLineWrap() && ((unsigned)CON_CUR_X + cbString) >= (unsigned)CON_COLS) {
357 		// Take care of the last line last colum exception...
358 		// The display scrolls up if you use the normal char out
359 		//   function even if you only write to the last place
360 		//   on the line. :-(
361 		if ((unsigned)CON_CUR_Y >= (unsigned)CON_HEIGHT) {
362 			unsigned long iFakeResult = cbString;
363 			cbString = CON_COLS - CON_CUR_X - 1;
364 
365 			// FIX ME !!! This will avoid the exception when cbString
366 			// is <= 0 but still doesn't work :-(
367 			if (cbString > 0)
368 				WriteConsole(hConsole, pszString, cbString, &Result, 0);
369 
370 			COORD dwBufferCoord;
371 			dwBufferCoord.X = 0;
372 			dwBufferCoord.Y = 0;
373 
374 			CHAR_INFO ciBuffer;
375 			ciBuffer.Char.AsciiChar = *(pszString+cbString);
376 			ciBuffer.Attributes = wAttributes;
377 			SMALL_RECT srWriteRegion;
378 			srWriteRegion.Top =		(SHORT) CON_BOTTOM;
379 			srWriteRegion.Bottom =	(SHORT) CON_BOTTOM;
380 			srWriteRegion.Left =	(SHORT) CON_RIGHT;
381 			srWriteRegion.Right =	(SHORT) CON_RIGHT;
382 
383 			COORD bufSize = {1,1};
384 
385 			WriteConsoleOutput(hConsole, &ciBuffer, bufSize,
386 				dwBufferCoord, &srWriteRegion);
387 
388 			// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
389 			ConsoleInfo.dwCursorPosition.X = CON_RIGHT;
390 
391 			return iFakeResult; // Skip the chars that did not fit
392 		}
393 		// just write the line up to the end
394 		else {
395 			int iFakeResult = cbString;
396 			cbString = CON_COLS - CON_CUR_X;
397 
398 			if(cbString > 0) {
399 				WriteConsole(hConsole, pszString, cbString, &Result, 0);
400 
401 				// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
402 				ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
403 			}
404 
405 			return iFakeResult; // Skip the chars that did not fit
406 		}
407 	} else {
408 		// If custom scrolling is enabled we must take care of it
409 		if(iScrollStart != -1 || iScrollEnd != -1) {
410 			return WriteString(pszString, cbString);
411 		}
412 
413 		// Apparently VT100 terminals have an invisible "81st" column that
414 		// can hold a cursor until another character is printed.  I'm not sure
415 		// exactly how to handle this, but here's a hack (Paul Brannan 5/28/98)
416 		if(ini.get_vt100_mode() && cbString + (unsigned)CON_CUR_X == (unsigned)CON_COLS) {
417 
418 			cbString--;
419 			if((long)cbString >= 0) WriteConsole(hConsole, pszString, cbString, &Result, 0);
420 
421 			COORD dwBufferCoord;
422 			dwBufferCoord.X = 0;
423 			dwBufferCoord.Y = 0;
424 
425 			CHAR_INFO ciBuffer;
426 			ciBuffer.Char.AsciiChar = *(pszString+cbString);
427 			ciBuffer.Attributes = wAttributes;
428 			SMALL_RECT srWriteRegion;
429 			srWriteRegion.Top =    (SHORT) ConsoleInfo.dwCursorPosition.Y;
430 			srWriteRegion.Bottom = (SHORT) ConsoleInfo.dwCursorPosition.Y;
431 			srWriteRegion.Left =   (SHORT) CON_RIGHT;
432 			srWriteRegion.Right =  (SHORT) CON_RIGHT;
433 
434 			COORD bufSize = {1,1};
435 
436 			WriteConsoleOutput(hConsole, &ciBuffer, bufSize,
437 				dwBufferCoord, &srWriteRegion);
438 
439 			// Update the ConsoleInfo struct
440 			ConsoleInfo.dwCursorPosition.X = CON_RIGHT + 1;
441 
442 			return Result + 1;
443 		}
444 
445 		// normal line will wrap normally or not to the end of buffer
446 		WriteConsole(hConsole, pszString, cbString, &Result, 0);
447 
448 		// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
449 		// FIX ME!!! This is susceptible to the same problem as above.
450 		// (e.g. we write out 160 characters)
451 		ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
452 		while(CON_CUR_X > CON_WIDTH) {
453 			ConsoleInfo.dwCursorPosition.X -= ConsoleInfo.dwSize.X;
454 			if((unsigned)CON_CUR_Y < (unsigned)CON_HEIGHT) {
455 				ConsoleInfo.dwCursorPosition.Y++;
456 			} else {
457 				// If we aren't at the bottom of the window, then we need to
458 				// scroll down (Paul Brannan 4/14/2000)
459 				if(ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) {
460 					ConsoleInfo.srWindow.Top++;
461 					ConsoleInfo.srWindow.Bottom++;
462 					ConsoleInfo.dwCursorPosition.Y++;
463 					SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow);
464 				}
465 			}
466 		}
467 	}
468 
469 	return Result;
470 
471 }
472 
473 unsigned long TConsole::WriteString(const char* pszString, unsigned long cbString) {
474 	DWORD Result = 0;
475 
476 	SetConsoleTextAttribute(hConsole, wAttributes);
477 
478 	//check to see if the line is longer than the display
479 	if (!getLineWrap()){
480 		unsigned long iFakeResult = cbString;
481 		if((CON_CUR_X + cbString) >= (unsigned int)CON_COLS)
482 			cbString = CON_COLS - CON_CUR_X;
483 		if(cbString > 0)
484 			Result = WriteText(pszString, cbString);
485 
486 		// We need to update the ConsoleInfo struct now (Paul Brannan 5/9/98)
487 		ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
488 		SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);
489 
490 		return iFakeResult; // Skip the chars that did not fit
491 	} else {
492 		// Write up to the end of the line
493 		unsigned long temp = cbString;
494 		if((CON_CUR_X + temp) > (unsigned int)CON_COLS) {
495 			temp = CON_COLS - CON_CUR_X;
496 		} else {
497 			Result = WriteText(pszString, temp);
498 			ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
499 			SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);
500 			return Result;
501 		}
502 		if(temp > 0) {
503 			Result = WriteText(pszString, temp);
504 			ConsoleInfo.dwCursorPosition.X += (unsigned short)Result;
505 			temp = (unsigned short)Result;
506 		}
507 
508 		// keep writing lines until we get to less than 80 chars left
509 		while((temp + (unsigned int)CON_COLS) < cbString) {
510 			index(); // LF
511 			ConsoleInfo.dwCursorPosition.X = 0; // CR
512 			Result = WriteText(&pszString[temp], CON_COLS);
513 			temp += (unsigned short)Result;
514 		}
515 
516 		// write out the last bit
517 		if(temp < cbString) {
518 			index();
519 			ConsoleInfo.dwCursorPosition.X = 0;
520 			Result = WriteText(&pszString[temp], cbString - temp);
521 			temp += (unsigned short)Result;
522 		}
523 
524 		// Apparently VT100 terminals have an invisible "81st" column that
525 		// can hold a cursor until another character is printed.  I'm not sure
526 		// exactly how to handle this, but here's a hack (Paul Brannan 5/28/98)
527 		if(!ini.get_vt100_mode() && cbString + (unsigned)ConsoleInfo.dwCursorPosition.X
528 			== (unsigned int)CON_COLS) {
529 			index();
530 			ConsoleInfo.dwCursorPosition.X = 0;
531 		}
532 
533 		SetConsoleCursorPosition(hConsole, ConsoleInfo.dwCursorPosition);
534 
535 		return temp;
536 	}
537 
538 	return 0;
539 }
540 
541 // This is for multi-character control strings (Paul Brannan 6/26/98)
542 unsigned long TConsole::WriteCtrlString(const char *pszString, unsigned long cbString) {
543 	unsigned long total = 0;
544 	while(total < cbString) {
545 		unsigned long Result = WriteCtrlChar(*(pszString + total));
546 		if(Result == 0) {
547 			Result = WriteStringFast(pszString + total, 1);
548 			if(Result == 0) return total;
549 		}
550 		total += Result;
551 	}
552 	return total;
553 }
554 
555 // This is for printing single control characters
556 // WriteCtrlString uses this (Paul Brannan 6/26/98)
557 unsigned long TConsole::WriteCtrlChar(char c) {
558 	// The console does not handel the CR/LF chars as we might expect
559 	// when using color. The attributes are not set correctly, so we
560 	// must interpret them manualy to preserve the colors on the screen.
561 
562 	unsigned long Result = 0; // just in case (Paul Brannan 6/26/98)
563 	switch (c) {
564     case '\x09': // horizontal tab
565 		SetCursorPosition((((CON_CUR_X/8)+1)*8), CON_CUR_Y);
566 		Result = 1;
567 		break;
568 
569     case '\x0a': // line feed
570 		index();
571 		Result = 1;
572 		break;
573     case '\x0d': // carrage return
574 		SetCursorPosition(CON_LEFT, CON_CUR_Y); // move to beginning of line
575 		Result = 1;
576 		break;
577 	case '\b': // backspace
578 		// Added support for backspace so the cursor position can be changed
579 		// (Paul Brannan 5/25/98)
580 		MoveCursorPosition(-1, 0);
581 		Result = 1;
582     default :    // else just write it like normal
583 		break;
584 	}
585 
586 	return Result;
587 }
588 
589 void TConsole::index() {
590 	// if on the last line scroll up
591 	// This must work with scrolling (Paul Brannan 5/13/98)
592 	if(iScrollEnd != -1 && (signed)CON_CUR_Y >= iScrollEnd) {
593 		ScrollDown(iScrollStart, iScrollEnd, -1);
594 	} else if ((iScrollEnd == -1 && (signed)CON_CUR_Y >= (signed)CON_HEIGHT)) {
595 		DWORD Result;
596 		WriteConsole(hConsole, "\n", 1, &Result, NULL);
597 
598 		// If we aren't at the bottom of the buffer, then we need to
599 		// scroll down (Paul Brannan 4/14/2000)
600 		if(iScrollEnd == -1 && ConsoleInfo.srWindow.Bottom < ConsoleInfo.dwSize.Y - 1) {
601 			ConsoleInfo.srWindow.Top++;
602 			ConsoleInfo.srWindow.Bottom++;
603 			ConsoleInfo.dwCursorPosition.Y++;
604 			// SetConsoleWindowInfo(hConsole, TRUE, &ConsoleInfo.srWindow);
605 		} else {
606 			ClearLine();
607 		}
608 	} else {     // else move cursor down to the next line
609 		SetCursorPosition(CON_CUR_X, CON_CUR_Y + 1);
610 	}
611 }
612 
613 void TConsole::reverse_index() {
614 	// if on the top line scroll down
615 	// This must work with scrolling (Paul Brannan 5/13/98)
616 	// We should be comparing against iScrollStart, not iScrollEnd (PB 12/2/98)
617 	if (iScrollStart == -1 && (signed)CON_CUR_Y <= 0) {
618 		ScrollDown(iScrollStart, -1, 1);
619 	} else if (iScrollStart != -1 && (signed)CON_CUR_Y <= iScrollStart) {
620 			ScrollDown(iScrollStart, iScrollEnd, 1);
621 	} else       // else move cursor up to the previous line
622 		SetCursorPosition(CON_CUR_X,CON_CUR_Y - 1);
623 }
624 
625 void TConsole::ScrollDown( int iStartRow , int iEndRow, int bUp ){
626 	CHAR_INFO ciChar;
627 	SMALL_RECT srScrollWindow;
628 
629 	// Correction from I.Ioannou 11 May 1997
630 	// check the scroll region
631 	if (iStartRow < iScrollStart) iStartRow = iScrollStart;
632 
633 	// Correction from I.Ioannou 11 May 1997
634 	// this will make Top the CON_TOP
635 	if ( iStartRow == -1) iStartRow = 0;
636 
637 	// Correction from I.Ioannou 18 Aug 97
638 	if ( iEndRow == -1)	{
639 		if ( iScrollEnd == -1 )
640 			iEndRow = CON_HEIGHT;
641 		else
642 			iEndRow = ((CON_HEIGHT <= iScrollEnd) ? CON_HEIGHT : iScrollEnd);
643 	}
644 	//
645 
646 	if ( iStartRow > CON_HEIGHT) iStartRow = CON_HEIGHT;
647 	if ( iEndRow > CON_HEIGHT) iEndRow = CON_HEIGHT;
648 
649 	srScrollWindow.Left =           (CON_LEFT);
650 	srScrollWindow.Right =  (SHORT) (CON_RIGHT);
651 	srScrollWindow.Top =    (SHORT) (CON_TOP + iStartRow );
652 	srScrollWindow.Bottom = (SHORT) (CON_TOP + iEndRow); // don't subtract 1 (PB 5/28)
653 
654 	ciChar.Char.AsciiChar = ' ';           // fill with spaces
655 	ciChar.Attributes = wAttributes;       // fill with current attrib
656 
657 	// This should speed things up (Paul Brannan 9/2/98)
658 	COORD dwDestOrg = {srScrollWindow.Left, srScrollWindow.Top + bUp};
659 
660 	// Note that iEndRow and iStartRow had better not be equal to -1 at this
661 	// point.  There are four cases to consider for out of bounds.  Two of
662 	// these cause the scroll window to be cleared; the others cause the
663 	// scroll region to be modified.  (Paul Brannan 12/3/98)
664 	if(dwDestOrg.Y > CON_TOP + iEndRow) {
665 		// We are scrolling past the end of the scroll region, so just
666 		// clear the window instead (Paul Brannan 12/3/98)
667 		ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow);
668 		return;
669 	} else if(dwDestOrg.Y + (iEndRow-iStartRow+1) < CON_TOP + iStartRow) {
670 		// We are scrolling past the end of the scroll region, so just
671 		// clear the window instead (Paul Brannan 12/3/98)
672 		ClearWindow(CON_TOP + iStartRow, CON_TOP + iEndRow);
673 		return;
674 	} else if(dwDestOrg.Y < CON_TOP + iStartRow) {
675 		// Modify the scroll region (Paul Brannan 12/3/98)
676 		dwDestOrg.Y = CON_TOP + iStartRow;
677 		srScrollWindow.Top -= bUp;
678 	} else 	if(dwDestOrg.Y + (iEndRow-iStartRow+1) > CON_TOP + iEndRow) {
679 		// Modify the scroll region (Paul Brannan 12/3/98)
680 		srScrollWindow.Bottom -= bUp;
681 	}
682 
683 	ScrollConsoleScreenBuffer(hConsole, &srScrollWindow,
684 		0, dwDestOrg, &ciChar);
685 }
686 
687 // This allows us to clear the screen with an arbitrary character
688 // (Paul Brannan 6/26/98)
689 void TConsole::ClearScreen(char c) {
690 	DWORD dwWritten;
691 	COORD Coord = {CON_LEFT, CON_TOP};
692 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
693 		(DWORD)(CON_LINES), Coord, &dwWritten);
694 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
695 		(DWORD)(CON_LINES), Coord, &dwWritten);
696 }
697 
698 // Same as clear screen, but only affects the scroll region
699 void TConsole::ClearWindow(int iStartRow, int iEndRow, char c) {
700 	DWORD dwWritten;
701 	COORD Coord = {CON_LEFT, CON_TOP + iStartRow};
702 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
703 		(DWORD)(iEndRow-iStartRow+1), Coord, &dwWritten);
704 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
705 		(DWORD)(CON_LINES), Coord, &dwWritten);
706 }
707 
708 // Clear from cursor to end of screen
709 void TConsole::ClearEOScreen(char c)
710 {
711 	DWORD dwWritten;
712 	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y + 1};
713 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
714 		(DWORD)(CON_HEIGHT - CON_CUR_Y), Coord, &dwWritten);
715 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
716 		(DWORD)(CON_LINES - CON_CUR_Y), Coord, &dwWritten);
717 	ClearEOLine();
718 }
719 
720 // Clear from beginning of screen to cursor
721 void TConsole::ClearBOScreen(char c)
722 {
723 	DWORD dwWritten;
724 	COORD Coord = {CON_LEFT, CON_TOP};
725 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS)*
726 		(DWORD)(CON_CUR_Y), Coord, &dwWritten);
727 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS)*
728 		(DWORD)(CON_CUR_Y), Coord, &dwWritten);
729 	ClearBOLine();
730 }
731 
732 void TConsole::ClearLine(char c)
733 {
734 	DWORD dwWritten;
735 	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y};
736 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_COLS),
737 		Coord, &dwWritten);
738 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_COLS),
739 		Coord, &dwWritten);
740 	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
741 }
742 
743 void TConsole::ClearEOLine(char c)
744 {
745 	DWORD dwWritten;
746 	COORD Coord = {CON_LEFT + CON_CUR_X, CON_TOP + CON_CUR_Y};
747 	FillConsoleOutputAttribute(hConsole, wAttributes,
748 		(DWORD)(CON_RIGHT - CON_CUR_X) +1, Coord, &dwWritten);
749 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_RIGHT - CON_CUR_X) +1,
750 		Coord, &dwWritten);
751 	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
752 }
753 
754 void TConsole::ClearBOLine(char c)
755 {
756 	DWORD dwWritten;
757 	COORD Coord = {CON_LEFT, CON_TOP + CON_CUR_Y};
758 	FillConsoleOutputCharacter(hConsole, c, (DWORD)(CON_CUR_X) + 1, Coord,
759 		&dwWritten);
760 	FillConsoleOutputAttribute(hConsole, wAttributes, (DWORD)(CON_CUR_X) + 1,
761 		Coord, &dwWritten);
762 	GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
763 }
764 
765 
766 //	Inserts blank lines to the cursor-y-position
767 //	scrolls down the rest. CURSOR MOVEMENT (to Col#1) ???
768 void TConsole::InsertLine(int numlines)
769 {
770 	COORD		to;
771 	SMALL_RECT	from;
772 	SMALL_RECT	clip;
773 	CHAR_INFO		fill;
774 	int		acty;
775 
776 	// Rest of screen would be deleted
777 	if ( (acty = GetCursorY()) >= CON_LINES - numlines ) {
778 		ClearEOScreen();    // delete rest of screen
779 		return;
780 	} /* IF */
781 
782 	//	Else scroll down the part of the screen which is below the
783 	//	cursor.
784 	from.Left =		CON_LEFT;
785 	from.Top =		CON_TOP + (SHORT)acty;
786 	from.Right =	CON_LEFT + (SHORT)CON_COLS;
787 	from.Bottom =	CON_TOP + (SHORT)CON_LINES;
788 
789 	clip = from;
790 	to.X = 0;
791 	to.Y = (SHORT)(from.Top + numlines);
792 
793 	fill.Char.AsciiChar = ' ';
794 	fill.Attributes = 7; 		// WHICH ATTRIBUTES TO TAKE FOR BLANK LINE ??
795 
796 	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
797 } /* InsertLine */
798 
799 //	Inserts blank characters under the cursor
800 void TConsole::InsertCharacter(int numchar)
801 {
802 	int		actx;
803 	SMALL_RECT	from;
804 	SMALL_RECT	clip;
805 	COORD			to;
806 	CHAR_INFO		fill;
807 
808 	if ( (actx = GetCursorX()) >= CON_COLS - numchar ) {
809 		ClearEOLine();
810 		return;
811 	} /* IF */
812 
813 	from.Left =		CON_LEFT + (SHORT)actx;
814 	from.Top =		CON_TOP + (SHORT)GetCursorY();
815 	from.Right =	CON_LEFT + (SHORT)CON_COLS;
816 	from.Bottom =	CON_TOP + (SHORT)from.Top;
817 
818 	clip = from;
819 	to.X = (SHORT)(actx + numchar);
820 	to.Y = from.Top;
821 
822 	fill.Char.AsciiChar = ' ';
823 	fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ??
824 
825 	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
826 } /* InsertCharacter */
827 
828 // Deletes characters under the cursor
829 // Note that there are cases in which all the following lines should shift by
830 // a character, but we don't handle these.  This could break some
831 // VT102-applications, but it shouldn't be too much of an issue.
832 void TConsole::DeleteCharacter(int numchar)
833 {
834 	int		actx;
835 	SMALL_RECT	from;
836 	SMALL_RECT	clip;
837 	COORD			to;
838 	CHAR_INFO		fill;
839 
840 	if ( (actx = GetCursorX()) >= CON_COLS - numchar ) {
841 		ClearEOLine();
842 		return;
843 	} /* IF */
844 
845 	from.Left =		CON_LEFT + (SHORT)actx;
846 	from.Top =		CON_TOP + (SHORT)GetCursorY();
847 	from.Right =	CON_LEFT + (SHORT)CON_COLS;
848 	from.Bottom =	CON_TOP + from.Top;
849 
850 	clip = from;
851 	to.X = (SHORT)(actx - numchar);
852 	to.Y = from.Top;
853 
854 	fill.Char.AsciiChar = ' ';
855 	fill.Attributes = wAttributes; // WHICH ATTRIBUTES TO TAKE FOR BLANK CHAR ??
856 
857 	ScrollConsoleScreenBuffer(hConsole, &from, &clip, to, &fill);
858 } /* DeleteCharacter */
859 
860 void TConsole::SetRawCursorPosition(int x, int y) {
861 	if (x > CON_WIDTH)  x = CON_WIDTH;
862 	if (x < 0)			x = 0;
863 	if (y > CON_HEIGHT)	y = CON_HEIGHT;
864 	if (y < 0)			y = 0;
865 	COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)};
866 	SetConsoleCursorPosition(hConsole, Coord);
867 
868 	// Update the ConsoleInfo struct (Paul Brannan 5/9/98)
869 	ConsoleInfo.dwCursorPosition.Y = Coord.Y;
870 	ConsoleInfo.dwCursorPosition.X = Coord.X;
871 
872 	// bug fix in case we went too far (Paul Brannan 5/25/98)
873 	if(ConsoleInfo.dwCursorPosition.X < CON_LEFT)
874 		ConsoleInfo.dwCursorPosition.X = CON_LEFT;
875 	if(ConsoleInfo.dwCursorPosition.X > CON_RIGHT)
876 		ConsoleInfo.dwCursorPosition.X = CON_RIGHT;
877 	if(ConsoleInfo.dwCursorPosition.Y < CON_TOP)
878 		ConsoleInfo.dwCursorPosition.Y = CON_TOP;
879 	if(ConsoleInfo.dwCursorPosition.Y > CON_BOTTOM)
880 		ConsoleInfo.dwCursorPosition.Y = CON_BOTTOM;
881 }
882 
883 // The new SetCursorPosition takes scroll regions into consideration
884 // (Paul Brannan 6/27/98)
885 void TConsole::SetCursorPosition(int x, int y) {
886 	if (x > CON_WIDTH)  x = CON_WIDTH;
887 	if (x < 0)			x = 0;
888 	if(iScrollEnd != -1) {
889 		if(y > iScrollEnd)		y = iScrollEnd;
890 	} else {
891 		if(y > CON_HEIGHT)		y = CON_HEIGHT;
892 	}
893 	if(iScrollStart != -1) {
894 		if(y < iScrollStart)	y = iScrollStart;
895 	} else {
896 		if(y < 0)				y = 0;
897 	}
898 
899 	COORD Coord = {(short)(CON_LEFT + x), (short)(CON_TOP + y)};
900 	SetConsoleCursorPosition(hConsole, Coord);
901 
902 	// Update the ConsoleInfo struct
903 	ConsoleInfo.dwCursorPosition.Y = Coord.Y;
904 	ConsoleInfo.dwCursorPosition.X = Coord.X;
905 }
906 
907 void TConsole::MoveCursorPosition(int x, int y) {
908 	SetCursorPosition(CON_CUR_X + x, CON_CUR_Y + y);
909 }
910 
911 void TConsole::SetExtendedMode(int iFunction, BOOL bEnable)
912 {
913 	// Probably should do something here...
914 	// Should change the screen mode, but do we need this?
915 }
916 
917 void TConsole::SetScroll(int start, int end) {
918 	iScrollStart = start;
919 	iScrollEnd = end;
920 }
921 
922 void TConsole::Beep() {
923 	if(ini.get_do_beep()) {
924 		if(!ini.get_speaker_beep()) printit("\a");
925 		else ::Beep(400, 100);
926 	}
927 }
928 
929 void TConsole::SetCursorSize(int pct) {
930 	CONSOLE_CURSOR_INFO ci = {(pct != 0)?pct:1, pct != 0};
931 	SetConsoleCursorInfo(hConsole, &ci);
932 }
933 
934 void saveScreen(CHAR_INFO *chiBuffer) {
935 	HANDLE hStdout;
936 	CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
937 	SMALL_RECT srctReadRect;
938 	COORD coordBufSize;
939 	COORD coordBufCoord;
940 
941 	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
942 	GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
943 
944     srctReadRect.Top = CON_TOP;    /* top left: row 0, col 0  */
945     srctReadRect.Left = CON_LEFT;
946     srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */
947     srctReadRect.Right = CON_RIGHT;
948 
949     coordBufSize.Y = CON_BOTTOM-CON_TOP+1;
950     coordBufSize.X = CON_RIGHT-CON_LEFT+1;
951 
952     coordBufCoord.X = CON_TOP;
953     coordBufCoord.Y = CON_LEFT;
954 
955     ReadConsoleOutput(
956 		hStdout,        /* screen buffer to read from       */
957 		chiBuffer,      /* buffer to copy into              */
958 		coordBufSize,   /* col-row size of chiBuffer        */
959 
960 		coordBufCoord,  /* top left dest. cell in chiBuffer */
961 		&srctReadRect); /* screen buffer source rectangle   */
962 }
963 
964 void restoreScreen(CHAR_INFO *chiBuffer) {
965 	HANDLE hStdout;
966 	CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
967 	SMALL_RECT srctReadRect;
968 	COORD coordBufSize;
969 	COORD coordBufCoord;
970 
971 	hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
972 	GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
973 
974 	// restore screen
975     srctReadRect.Top = CON_TOP;    /* top left: row 0, col 0  */
976     srctReadRect.Left = CON_LEFT;
977     srctReadRect.Bottom = CON_BOTTOM; /* bot. right: row 1, col 79 */
978     srctReadRect.Right = CON_RIGHT;
979 
980     coordBufSize.Y = CON_BOTTOM-CON_TOP+1;
981     coordBufSize.X = CON_RIGHT-CON_LEFT+1;
982 
983     coordBufCoord.X = CON_TOP;
984     coordBufCoord.Y = CON_LEFT;
985     WriteConsoleOutput(
986         hStdout, /* screen buffer to write to    */
987         chiBuffer,        /* buffer to copy from          */
988         coordBufSize,     /* col-row size of chiBuffer    */
989         coordBufCoord, /* top left src cell in chiBuffer  */
990         &srctReadRect); /* dest. screen buffer rectangle */
991 	// end restore screen
992 
993 }
994 
995 CHAR_INFO* newBuffer() {
996     CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
997     HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
998     GetConsoleScreenBufferInfo(hStdout, &ConsoleInfo);
999     CHAR_INFO * chiBuffer;
1000     chiBuffer = new CHAR_INFO[(CON_BOTTOM-CON_TOP+1)*(CON_RIGHT-CON_LEFT+1)];
1001 	return chiBuffer;
1002 }
1003 
1004 void deleteBuffer(CHAR_INFO* chiBuffer) {
1005 	delete[] chiBuffer;
1006 }
1007 
1008