1 /* HCONSOLE.C   (c) Copyright "Fish" (David B. Trout), 2005-2009     */
2 /*              Hercules console panel support functions             */
3 
4 //////////////////////////////////////////////////////////////////////////////////////////
5 // (c) Copyright "Fish" (David B. Trout), 2005-2009. Released under the Q Public License
6 // (http://www.hercules-390.org/herclic.html) as modifications to Hercules.
7 //////////////////////////////////////////////////////////////////////////////////////////
8 
9 #include "hstdinc.h"
10 
11 #include "hercules.h"
12 #include "hconsole.h"
13 
14 //////////////////////////////////////////////////////////////////////////////////////////
15 //////////////////////////////////////////////////////////////////////////////////////////
16 //////////////////////////////////////////////////////////////////////////////////////////
17 
18 #if defined( _MSVC_ )
19 
20 //////////////////////////////////////////////////////////////////////////////////////////
21 // 'save_and_set' = 1 --> just what it says; 0 --> restore from saved value.
22 
23 static DWORD g_dwConsoleInputMode  = 0;     // (saved value so we can later restore it)
24 static DWORD g_dwConsoleOutputMode = 0;     // (saved value so we can later restore it)
25 static WORD  g_wDefaultAttrib      = 0;     // (saved value so we can later restore it)
26 
default_FG_color()27 static WORD default_FG_color() { return  g_wDefaultAttrib       & 0x0F; }
default_BG_color()28 static WORD default_BG_color() { return (g_wDefaultAttrib >> 4) & 0x0F; }
29 
set_or_reset_console_mode(int keybrd_fd,short save_and_set)30 int set_or_reset_console_mode( int keybrd_fd, short save_and_set )
31 {
32     CONSOLE_SCREEN_BUFFER_INFO  csbi;
33     HANDLE  hStdIn, hStdErr;
34     DWORD   dwNewInputMode;
35     DWORD   dwNewOutputMode;
36 
37     if ( ! _isatty( keybrd_fd ) )
38     {
39         errno = EBADF;
40         return -1;
41     }
42 
43     hStdIn  = (HANDLE) _get_osfhandle( keybrd_fd );
44     ASSERT( hStdIn && INVALID_HANDLE_VALUE != hStdIn );
45 
46     hStdErr  = GetStdHandle( STD_ERROR_HANDLE );
47     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
48 
49     if ( save_and_set )
50     {
51         VERIFY( GetConsoleMode( hStdIn,  &g_dwConsoleInputMode  ) );
52         VERIFY( GetConsoleMode( hStdErr, &g_dwConsoleOutputMode ) );
53         VERIFY( GetConsoleScreenBufferInfo( hStdErr, &csbi ) );
54         g_wDefaultAttrib = csbi.wAttributes;
55         dwNewInputMode  = 0;
56         dwNewOutputMode = 0;
57     }
58     else // (restore/reset)
59     {
60         VERIFY( SetConsoleTextAttribute( hStdErr, g_wDefaultAttrib ) );
61         dwNewInputMode  = g_dwConsoleInputMode;
62         dwNewOutputMode = g_dwConsoleOutputMode;
63     }
64 
65     VERIFY( SetConsoleMode( hStdIn,  dwNewInputMode  ) );
66     VERIFY( SetConsoleMode( hStdErr, dwNewOutputMode ) );
67 
68     return 0;
69 }
70 
71 //////////////////////////////////////////////////////////////////////////////////////////
72 // Translate Herc color to Win32 color...
73 
74 #define  W32_FOREGROUND_COLOR( w32_color )    ( ( w32_color )      )
75 #define  W32_BACKGROUND_COLOR( w32_color )    ( ( w32_color ) << 4 )
76 
77 #define  W32_COLOR_BLACK           ( 0 )
78 #define  W32_COLOR_RED             ( FOREGROUND_RED   )
79 #define  W32_COLOR_GREEN           ( FOREGROUND_GREEN )
80 #define  W32_COLOR_BLUE            ( FOREGROUND_BLUE  )
81 #define  W32_COLOR_CYAN            ( FOREGROUND_GREEN | FOREGROUND_BLUE  )
82 #define  W32_COLOR_MAGENTA         ( FOREGROUND_RED   | FOREGROUND_BLUE  )
83 #define  W32_COLOR_YELLOW          ( FOREGROUND_RED   | FOREGROUND_GREEN )
84 #define  W32_COLOR_LIGHT_GREY      ( FOREGROUND_RED   | FOREGROUND_GREEN | FOREGROUND_BLUE )
85 
86 #define  W32_COLOR_DARK_GREY       ( FOREGROUND_INTENSITY | W32_COLOR_BLACK     )
87 #define  W32_COLOR_LIGHT_RED       ( FOREGROUND_INTENSITY | W32_COLOR_RED        )
88 #define  W32_COLOR_LIGHT_GREEN     ( FOREGROUND_INTENSITY | W32_COLOR_GREEN      )
89 #define  W32_COLOR_LIGHT_BLUE      ( FOREGROUND_INTENSITY | W32_COLOR_BLUE       )
90 #define  W32_COLOR_LIGHT_CYAN      ( FOREGROUND_INTENSITY | W32_COLOR_CYAN       )
91 #define  W32_COLOR_LIGHT_MAGENTA   ( FOREGROUND_INTENSITY | W32_COLOR_MAGENTA    )
92 #define  W32_COLOR_LIGHT_YELLOW    ( FOREGROUND_INTENSITY | W32_COLOR_YELLOW     )
93 #define  W32_COLOR_WHITE           ( FOREGROUND_INTENSITY | W32_COLOR_LIGHT_GREY )
94 
W32_COLOR(short herc_color)95 static WORD W32_COLOR( short herc_color )
96 {
97     switch ( herc_color )
98     {
99         case COLOR_BLACK:         return W32_COLOR_BLACK;
100         case COLOR_RED:           return W32_COLOR_RED;
101         case COLOR_GREEN:         return W32_COLOR_GREEN;
102         case COLOR_BLUE:          return W32_COLOR_BLUE;
103         case COLOR_CYAN:          return W32_COLOR_CYAN;
104         case COLOR_MAGENTA:       return W32_COLOR_MAGENTA;
105         case COLOR_YELLOW:        return W32_COLOR_YELLOW;
106         case COLOR_DARK_GREY:     return W32_COLOR_DARK_GREY;
107 
108         case COLOR_LIGHT_GREY:    return W32_COLOR_LIGHT_GREY;
109         case COLOR_LIGHT_RED:     return W32_COLOR_LIGHT_RED;
110         case COLOR_LIGHT_GREEN:   return W32_COLOR_LIGHT_GREEN;
111         case COLOR_LIGHT_BLUE:    return W32_COLOR_LIGHT_BLUE;
112         case COLOR_LIGHT_CYAN:    return W32_COLOR_LIGHT_CYAN;
113         case COLOR_LIGHT_MAGENTA: return W32_COLOR_LIGHT_MAGENTA;
114         case COLOR_LIGHT_YELLOW:  return W32_COLOR_LIGHT_YELLOW;
115         case COLOR_WHITE:         return W32_COLOR_WHITE;
116 
117         case COLOR_DEFAULT_BG:    return default_BG_color();
118         case COLOR_DEFAULT_FG:    return default_FG_color();
119         case COLOR_DEFAULT_LIGHT: return default_FG_color() | FOREGROUND_INTENSITY;
120 
121         default:                  return default_FG_color();
122     }
123 }
124 
125 //////////////////////////////////////////////////////////////////////////////////////////
126 
set_screen_color(FILE * confp,short herc_fore,short herc_back)127 int set_screen_color ( FILE* confp, short herc_fore, short herc_back )
128 {
129     HANDLE  hStdErr;
130     WORD    wColor;
131     int     cons_fd;
132 
133     if ( !confp )
134     {
135         errno = EINVAL;
136         return -1;
137     }
138 
139     if ( ! _isatty( cons_fd = fileno( confp ) ) )
140     {
141         errno = EBADF;
142         return -1;
143     }
144 
145     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
146     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
147 
148     wColor =
149         0
150         | W32_FOREGROUND_COLOR( W32_COLOR( herc_fore ) )
151         | W32_BACKGROUND_COLOR( W32_COLOR( herc_back ) )
152         ;
153 
154     VERIFY( SetConsoleTextAttribute( hStdErr, wColor ) );
155 
156     return 0;
157 }
158 
159 //////////////////////////////////////////////////////////////////////////////////////////
160 // screen positions are 1-based; row 1 == top line; col 1 == leftmost column
161 
set_screen_pos(FILE * confp,short rowY1,short colX1)162 int set_screen_pos( FILE* confp, short rowY1, short colX1 )
163 {
164     CONSOLE_SCREEN_BUFFER_INFO  csbi;
165     HANDLE  hStdErr;
166     COORD   ptConsole;
167     int     cons_fd;
168 
169     if ( !confp )
170     {
171         errno = EINVAL;
172         return -1;
173     }
174 
175     if ( ! _isatty( cons_fd = fileno( confp ) ) )
176     {
177         errno = EBADF;
178         return -1;
179     }
180 
181     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
182     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
183 
184     VERIFY( GetConsoleScreenBufferInfo( hStdErr, &csbi ) );
185 
186     // Note: ANSI escape codes are 1-based, whereas
187     // SetConsoleCursorPosition values are 0-based...
188 
189     if (0
190         || colX1 < 1 || colX1 > csbi.dwSize.X
191         || rowY1 < 1 || rowY1 > csbi.dwSize.Y
192     )
193     {
194         errno = EINVAL;
195         return -1;
196     }
197 
198     ptConsole.X = colX1 - 1;
199     ptConsole.Y = rowY1 - 1;
200 
201     VERIFY( SetConsoleCursorPosition( hStdErr, ptConsole ) );
202 
203     return 0;
204 }
205 
206 //////////////////////////////////////////////////////////////////////////////////////////
207 // (From KB article 99261)
208 
clear_screen(FILE * confp)209 int clear_screen( FILE* confp )
210 {
211     CONSOLE_SCREEN_BUFFER_INFO  csbi;
212     HANDLE  hStdErr;
213     DWORD   dwNumCells, dwCellsWritten;
214     COORD   ptConsole = { 0, 0 };
215     int     cons_fd;
216 
217     if ( !confp )
218     {
219         errno = EINVAL;
220         return -1;
221     }
222 
223     if ( ! _isatty( cons_fd = fileno( confp ) ) )
224     {
225         errno = EBADF;
226         return -1;
227     }
228 
229     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
230     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
231 
232     VERIFY( GetConsoleScreenBufferInfo( hStdErr, &csbi ) ); dwNumCells = csbi.dwSize.X * csbi.dwSize.Y;
233     VERIFY( FillConsoleOutputCharacter( hStdErr, ' ', dwNumCells, ptConsole, &dwCellsWritten ) );
234     VERIFY( GetConsoleScreenBufferInfo( hStdErr, &csbi ) );
235     VERIFY( FillConsoleOutputAttribute( hStdErr, csbi.wAttributes, dwNumCells, ptConsole, &dwCellsWritten ) );
236     VERIFY( SetConsoleCursorPosition  ( hStdErr, ptConsole ) );
237 
238     return 0;
239 }
240 
241 //////////////////////////////////////////////////////////////////////////////////////////
242 
erase_to_eol(FILE * confp)243 int erase_to_eol( FILE* confp )
244 {
245     CONSOLE_SCREEN_BUFFER_INFO  csbi;
246     HANDLE  hStdErr;
247     DWORD   dwCellsWritten;
248     COORD   ptConsole;
249     int     cons_fd;
250 
251     if ( !confp )
252     {
253         errno = EINVAL;
254         return -1;
255     }
256 
257     if ( ! _isatty( cons_fd = fileno( confp ) ) )
258     {
259         errno = EBADF;
260         return -1;
261     }
262 
263     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
264     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
265 
266     VERIFY( GetConsoleScreenBufferInfo( hStdErr, &csbi ) ); ptConsole = csbi.dwCursorPosition;
267     VERIFY( FillConsoleOutputAttribute( hStdErr, csbi.wAttributes, csbi.dwSize.X - ptConsole.X, ptConsole, &dwCellsWritten ) );
268     VERIFY( FillConsoleOutputCharacter( hStdErr,     ' ',          csbi.dwSize.X - ptConsole.X, ptConsole, &dwCellsWritten ) );
269 
270     return 0;
271 }
272 
273 //////////////////////////////////////////////////////////////////////////////////////////
274 // Handles key presses that result in TWO characters being generated...
275 
translate_keystroke(char kbbuf[],int * pkblen)276 void translate_keystroke( char kbbuf[], int* pkblen )
277 {
278     BYTE ch = kbbuf[0];         // (move char to work var)
279 
280     switch ( ch )               // Check if special key pressed...
281     {
282         case 0x0D:              // enter key
283             kbbuf[0] = '\n';    // change to newline character
284                                 // fall thru to default case
285         default:                // no further translation needed
286             break;              // accept the keystroke as-is
287 
288         // translate special key (escape sequence)...
289 
290         case 0x00:              // 1st char of special key press
291         case 0xE0:              // 1st char of special key press
292         {
293             BYTE orig_ch, ch2;
294 
295             if ( !kbhit() )     // if not two chars generated,
296                 break;          // then not special key press
297 
298             orig_ch = ch;       // save original keystroke
299             ch = '\x1B';        // change it to an escape char
300             ch2 = getch();      // get second keystroke of pair
301 
302             switch ( ch2 )      // generate ANSI escape sequence
303             {
304                 case 0x47: strcpy( kbbuf, KBD_HOME            ); break;
305                 case 0x52: strcpy( kbbuf, KBD_INSERT          ); break;
306                 case 0x53: strcpy( kbbuf, KBD_DELETE          ); break;
307                 case 0x4F: strcpy( kbbuf, KBD_END             ); break;
308                 case 0x49: strcpy( kbbuf, KBD_PAGE_UP         ); break;
309                 case 0x51: strcpy( kbbuf, KBD_PAGE_DOWN       ); break;
310 
311                 case 0x48: strcpy( kbbuf, KBD_UP_ARROW        ); break;
312                 case 0x50: strcpy( kbbuf, KBD_DOWN_ARROW      ); break;
313                 case 0x4D: strcpy( kbbuf, KBD_RIGHT_ARROW     ); break;
314                 case 0x4B: strcpy( kbbuf, KBD_LEFT_ARROW      ); break;
315 
316                 case 0x77: strcpy( kbbuf, KBD_CTRL_HOME       ); break;
317                 case 0x75: strcpy( kbbuf, KBD_CTRL_END        ); break;
318 
319                 case 0x8D: strcpy( kbbuf, KBD_CTRL_UP_ARROW   ); break;
320                 case 0x91: strcpy( kbbuf, KBD_CTRL_DOWN_ARROW ); break;
321 
322                 case 0x98: strcpy( kbbuf, KBD_ALT_UP_ARROW    ); break;
323                 case 0xA0: strcpy( kbbuf, KBD_ALT_DOWN_ARROW  ); break;
324                 case 0x9D: strcpy( kbbuf, KBD_ALT_RIGHT_ARROW ); break;
325                 case 0x9B: strcpy( kbbuf, KBD_ALT_LEFT_ARROW  ); break;
326 
327                 default:
328                 {
329 #if 0
330                     kbbuf[0] = '\x1B';
331                     kbbuf[1] = ch2;
332                     kbbuf[2] = 0;
333 #else
334                     /* EAT IT */
335                     kbbuf[0] = 0;
336                     kbbuf[1] = 0;
337                     kbbuf[2] = 0;
338 #endif
339                     break;
340 
341                 } // end default
342 
343             } // end switch( ch2 )
344 
345             *pkblen = strlen( kbbuf );      // inform caller #of chars
346             break;
347 
348         } // end case: 0x00, 0xE0
349 
350     } // end switch( ch )
351 }
352 
353 //////////////////////////////////////////////////////////////////////////////////////////
354 
console_beep(FILE * confp)355 int console_beep( FILE* confp )
356 {
357     int cons_fd;
358 
359     if ( !confp )
360     {
361         errno = EINVAL;
362         return -1;
363     }
364 
365     if ( ! _isatty( cons_fd = fileno( confp ) ) )
366     {
367         errno = EBADF;
368         return -1;
369     }
370 
371     MessageBeep(-1);
372 
373     return 0;
374 }
375 
376 //////////////////////////////////////////////////////////////////////////////////////////
377 
378 #ifdef OPTION_EXTCURS
get_cursor_pos(int keybrd_fd,FILE * confp,short * row,short * col)379 int get_cursor_pos( int keybrd_fd, FILE* confp, short* row, short* col )
380 {
381     CONSOLE_SCREEN_BUFFER_INFO  csbi;
382     HANDLE  hStdErr;
383     int     cons_fd;
384 
385     UNREFERENCED( keybrd_fd );
386 
387     if ( !confp || !row || !col )
388     {
389         errno = EINVAL;
390         return -1;
391     }
392 
393     if ( ! _isatty( cons_fd = fileno( confp ) ) )
394     {
395         errno = EBADF;
396         return -1;
397     }
398 
399     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
400     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
401 
402     if ( !GetConsoleScreenBufferInfo( hStdErr, &csbi ) )
403     {
404         errno = EIO;
405         return -1;
406     }
407 
408     *row = 1 + csbi.dwCursorPosition.Y;
409     *col = 1 + csbi.dwCursorPosition.X;
410 
411     return 0;
412 }
413 #endif // OPTION_EXTCURS
414 
415 //////////////////////////////////////////////////////////////////////////////////////////
416 
get_console_dim(FILE * confp,int * rows,int * cols)417 int  get_console_dim( FILE* confp, int* rows, int* cols )
418 {
419     CONSOLE_SCREEN_BUFFER_INFO  csbi;
420     HANDLE  hStdErr;
421     int     cons_fd;
422 
423     if ( !confp || !rows || !cols )
424     {
425         errno = EINVAL;
426         return -1;
427     }
428 
429     *rows = *cols = 0;
430 
431     if ( ! _isatty( cons_fd = fileno( confp ) ) )
432     {
433         errno = EBADF;
434         return -1;
435     }
436 
437     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
438     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
439 
440     if ( !GetConsoleScreenBufferInfo( hStdErr, &csbi ) )
441     {
442         errno = EIO;
443         return -1;
444     }
445 
446     *rows = 1 + csbi.srWindow.Bottom - csbi.srWindow.Top;
447     *cols = 1 + csbi.srWindow.Right  - csbi.srWindow.Left;
448     return 0;
449 }
450 
451 //////////////////////////////////////////////////////////////////////////////////////////
452 
set_console_cursor_shape(FILE * confp,int ins)453 int  set_console_cursor_shape( FILE* confp, int ins )
454 {
455     CONSOLE_CURSOR_INFO  ci;
456     HANDLE  hStdErr;
457     int     cons_fd;
458 
459     if ( !confp )
460     {
461         errno = EINVAL;
462         return -1;
463     }
464 
465     if ( ! _isatty( cons_fd = fileno( confp ) ) )
466     {
467         errno = EBADF;
468         return -1;
469     }
470 
471     hStdErr = (HANDLE) _get_osfhandle( cons_fd );
472     ASSERT( hStdErr && INVALID_HANDLE_VALUE != hStdErr );
473 
474     ci.bVisible = TRUE;
475     ci.dwSize = ins ? 20 : 100; // (note: values are percent of cell height)
476 
477     if ( !SetConsoleCursorInfo( hStdErr, &ci ) )
478     {
479         errno = EIO;
480         return -1;
481     }
482 
483     return 0;
484 }
485 
486 //////////////////////////////////////////////////////////////////////////////////////////
487 // From KB article 124103: "How To Obtain a Console Window Handle (HWND)"
488 // http://support.microsoft.com/?kbid=124103
489 
490 #define MAX_WINDOW_TITLE_LEN  (256)   // (purely arbitrary)
491 
492 static TCHAR g_szOriginalTitle[ MAX_WINDOW_TITLE_LEN ] = {0};
493 
w32_set_console_title(char * pszTitle)494 int w32_set_console_title( char* pszTitle )
495 {
496     TCHAR    szNewTitleBuff [ MAX_WINDOW_TITLE_LEN ];
497     LPCTSTR  pszNewTitle = NULL;
498 
499     if (!g_szOriginalTitle[0])
500         VERIFY(GetConsoleTitle( g_szOriginalTitle, MAX_WINDOW_TITLE_LEN ));
501 
502     if (pszTitle)
503     {
504         _sntprintf( szNewTitleBuff, MAX_WINDOW_TITLE_LEN-1, _T("%hs"), pszTitle );
505         szNewTitleBuff[MAX_WINDOW_TITLE_LEN-1]=0;
506         pszNewTitle = szNewTitleBuff;
507     }
508     else
509         pszNewTitle = g_szOriginalTitle;
510 
511     if (!SetConsoleTitle( pszNewTitle ))
512     {
513         errno = GetLastError();
514         return -1;
515     }
516     return 0;
517 }
518 
519 //////////////////////////////////////////////////////////////////////////////////////////
520 //////////////////////////////////////////////////////////////////////////////////////////
521 //////////////////////////////////////////////////////////////////////////////////////////
522 
523 #else // !defined( WIN32 )
524 
525 #ifdef HAVE_TERMIOS_H
526 static struct termios saved_kbattr;  // (saved value so we can later restore it)
527 #endif
528 
529 // 'save_and_set' = 1 --> just what it says; 0 --> restore from saved value.
530 
set_or_reset_console_mode(int keybrd_fd,short save_and_set)531 int set_or_reset_console_mode( int keybrd_fd, short save_and_set )
532 {
533 #ifdef HAVE_TERMIOS_H
534 struct termios kbattr;
535 
536     if ( save_and_set )
537     {
538         // Put the terminal into cbreak mode
539 
540         tcgetattr( keybrd_fd, &saved_kbattr );      // (save for later restore)
541 
542         kbattr = saved_kbattr;                      // (make copy)
543 
544         kbattr.c_lflag     &=  ~(ECHO | ICANON);    // (set desired values...)
545         kbattr.c_cc[VMIN]   =         0;
546         kbattr.c_cc[VTIME]  =         0;
547 
548         tcsetattr( keybrd_fd, TCSANOW, &kbattr );
549     }
550     else
551     {
552         // Restore the terminal mode
553 
554         tcsetattr( STDIN_FILENO, TCSANOW, &saved_kbattr );  // (restore prev value)
555     }
556 
557 #else
558 
559     UNREFERENCED( keybrd_fd );
560     UNREFERENCED( save_and_set );
561 
562 #endif
563 
564     return 0;
565 }
566 
567 //////////////////////////////////////////////////////////////////////////////////////////
568 // screen positions are 1-based; row 1 == top line; col 1 == leftmost column
569 
set_screen_pos(FILE * confp,short rowY1,short colX1)570 int set_screen_pos( FILE* confp, short rowY1, short colX1 )
571 {
572 #define                      ANSI_POSITION_CURSOR    "\x1B[%d;%dH"
573 
574     return ( fprintf( confp, ANSI_POSITION_CURSOR, rowY1, colX1 ) ? 0 : -1 );
575 }
576 
577 //////////////////////////////////////////////////////////////////////////////////////////
578 
erase_to_eol(FILE * confp)579 int erase_to_eol( FILE* confp )
580 {
581 #define                      ANSI_ERASE_EOL    "\x1B[K"
582 
583     return ( fprintf( confp, ANSI_ERASE_EOL ) ? 0 : -1 );
584 }
585 
586 //////////////////////////////////////////////////////////////////////////////////////////
587 
clear_screen(FILE * confp)588 int clear_screen( FILE* confp )
589 {
590 #define                      ANSI_ERASE_SCREEN    "\x1B[2J"
591 
592     return ( fprintf( confp, ANSI_ERASE_SCREEN ) ? 0 : -1 );
593 }
594 
595 //////////////////////////////////////////////////////////////////////////////////////////
596 /*
597 From:  (http://www.vtt.fi/tte/EuroBridge/Xew/iso6429.html)
598 
599 CSI (Control Sequence Introducer) = "\033[" or "\233"
600 
601 SGR (Select Graphic Rendition) = CSI Ps1;Ps2;... m
602 
603 SGR (Select Graphic Rendition):
604 
605         CSI Ps ... 0x6d
606 
607         (Note: 0x6d = ASCII 'm')
608 
609     SGR is used to establish one or more graphic rendition aspects for
610     subsequent text. The established aspects remain in effect until the
611     next occurrence of SGR in the data stream, depending on the setting
612     of the GRAPHIC RENDITION COMBINATION MODE (GRCM).
613 
614     The implementation assumes the GRCM = CUMULATIVE (that is, each aspect
615     will be in effect until explicitly cancelled by another SGR).
616 
617     --------------------------------------------------------------------------
618     Ps     Description
619     --------------------------------------------------------------------------
620     0      default rendition; cancel all preceding occurrences of SGR;
621            invoke primary font.
622     1      bold or increased intensity
623 
624     2      faint   <------<<<    Fish: non-standard, but apparently anyway
625                                        (based on other documents I've seen)
626 
627     3      italicized
628     4      underlined
629     7      negative image
630     9      crossed out
631     10     primary (default) font
632     11-19  first to ninth alternative fonts (see also fonts)
633     21     doubly underlined
634     22     normal intensity (neither bold nor faint)
635     23     not italicized
636     24     not underlined (neither singly or doubly)
637     27     positive image
638     29     not crossed out
639     30-37  foreground color of the text; 30=black, 31=red, 32=green,
640            33=yellow, 34=blue, 35=magenta, 36=cyan, 37=white.
641     39     default foreground text color (foreground color of the widget)
642     40-47  background color of the text; 40=black, 41=red, 42=green,
643            43=yellow, 44=blue, 45=magenta, 46=cyan, 47=white.
644     49     default background text color (background color of the widget)
645     51     framed (see FrameRendition)
646     53     overlined [not implemented yet]
647     54     not framed, not encircled
648     55     not overlined [not implemented yet]
649     --------------------------------------------------------------------------
650 
651 
652     "\033[m"    Reset
653     "\033[0m"   Reset
654     "\033[1m"   Bold
655 
656     "\033[2m"   Faint  <------<<<   Fish: non-standard, but apparently anyway
657                                           (based on other documents I've seen)
658 
659     "\033[3m"   Italic
660     "\033[4m"   Underline
661     "\033[7m"   Inverse
662     "\033[9m"   Crossed out
663     "\033[10m"  primary font
664     "\033[11m"  1st alternate font
665     "\033[12m"  2nd alternate font
666     "\033[13m"  3rd alternate font
667     "\033[14m"  4th alternate font
668     "\033[15m"  5th alternate font
669     "\033[16m"  6th alternate font
670     "\033[17m"  7th alternate font
671     "\033[18m"  8th alternate font
672     "\033[19m"  9th alternate font
673     "\033[21m"  Double underline
674     "\033[22m"  Bold off
675     "\033[23m"  Italic off
676     "\033[24m"  Underline off (double or single)
677     "\033[27m"  Inverse off
678     "\033[29m"  Crossed out off
679 
680     "\033[30m"  Black foreground
681     "\033[31m"  Red foreground
682     "\033[32m"  Green foreground
683     "\033[33m"  Yellow foreground
684     "\033[34m"  Blue foreground
685     "\033[35m"  Magenta foreground
686     "\033[36m"  Cyan foreground
687     "\033[37m"  White foreground
688     "\033[39m"  Default foreground
689 
690     "\033[40m"  Black background
691     "\033[41m"  Red background
692     "\033[42m"  Green background
693     "\033[43m"  Yellow background
694     "\033[44m"  Blue background
695     "\033[45m"  Magenta background
696     "\033[46m"  Cyan background
697     "\033[47m"  White background
698     "\033[49m"  Default background
699 
700     "\033[51m"  Framed on
701     "\033[54m"  Framed off
702 */
703 //////////////////////////////////////////////////////////////////////////////////////////
704 // Translate Herc color to ANSI/ISO-6429 (ECMA-48) color...
705 // High-order byte is attribute (0=normal, 1=bold), low-order byte is color.
706 
707 #define  ISO_COLOR_BLACK     ( 30 )
708 #define  ISO_COLOR_RED       ( 31 )
709 #define  ISO_COLOR_GREEN     ( 32 )
710 #define  ISO_COLOR_YELLOW    ( 33 )
711 #define  ISO_COLOR_BLUE      ( 34 )
712 #define  ISO_COLOR_MAGENTA   ( 35 )
713 #define  ISO_COLOR_CYAN      ( 36 )
714 #define  ISO_COLOR_WHITE     ( 37 )
715 #define  ISO_COLOR_DEFAULT   ( 39 )
716 
717 #define  ISO_NORMAL( iso_color )    ( ( 0x0000 ) | ( (uint16_t)( iso_color ) ) )
718 #define  ISO_BRIGHT( iso_color )    ( ( 0x0100 ) | ( (uint16_t)( iso_color ) ) )
719 
720 #define  ISO_IS_ISO_BRIGHT( iso_color )       ( ( ( iso_color ) >> 8 ) & 0x01 )
721 
722 // PROGRAMMING NOTE: the '2' (faint/dim) and '22' (bold-off)
723 // attribute codes are UNRELIABLE. They are apparently rarely
724 // implemented properly on many platforms (Cygwin included).
725 // Thus we prefer to use the more reliable (but programmatically
726 // bothersome) '0' (reset) attribute instead [whenever we wish
727 // to paint a 'dim/faint' (non-bold) color]. As a result however,
728 // we need to be careful to paint the foregoround/background
729 // colors in the proper sequence/order and in the proper manner.
730 // See the below 'set_screen_color' function for details. (Fish)
731 
732 //#define  ISO_NORMAL_OR_BRIGHT( iso_color )    ISO_IS_ISO_BRIGHT( iso_color ) ? 1 : 22
733 //#define  ISO_NORMAL_OR_BRIGHT( iso_color )    ISO_IS_ISO_BRIGHT( iso_color ) ? 1 : 2
734 #define  ISO_NORMAL_OR_BRIGHT( iso_color )    ISO_IS_ISO_BRIGHT( iso_color ) ? 1 : 0
735 
736 #define  ISO_FOREGROUND_COLOR( iso_color )    ( ( ( iso_color ) & 0x00FF )      )
737 #define  ISO_BACKGROUND_COLOR( iso_color )    ( ( ( iso_color ) & 0x00FF ) + 10 )
738 
ISO_COLOR(short herc_color)739 static uint16_t ISO_COLOR( short herc_color )
740 {
741     switch ( herc_color )
742     {
743         case COLOR_BLACK:         return ISO_NORMAL( ISO_COLOR_BLACK   );
744         case COLOR_RED:           return ISO_NORMAL( ISO_COLOR_RED     );
745         case COLOR_GREEN:         return ISO_NORMAL( ISO_COLOR_GREEN   );
746         case COLOR_BLUE:          return ISO_NORMAL( ISO_COLOR_BLUE    );
747         case COLOR_CYAN:          return ISO_NORMAL( ISO_COLOR_CYAN    );
748         case COLOR_MAGENTA:       return ISO_NORMAL( ISO_COLOR_MAGENTA );
749         case COLOR_YELLOW:        return ISO_NORMAL( ISO_COLOR_YELLOW  );
750         case COLOR_DARK_GREY:     return ISO_BRIGHT( ISO_COLOR_BLACK   );
751 
752         case COLOR_LIGHT_GREY:    return ISO_NORMAL( ISO_COLOR_WHITE   );
753         case COLOR_LIGHT_RED:     return ISO_BRIGHT( ISO_COLOR_RED     );
754         case COLOR_LIGHT_GREEN:   return ISO_BRIGHT( ISO_COLOR_GREEN   );
755         case COLOR_LIGHT_BLUE:    return ISO_BRIGHT( ISO_COLOR_BLUE    );
756         case COLOR_LIGHT_CYAN:    return ISO_BRIGHT( ISO_COLOR_CYAN    );
757         case COLOR_LIGHT_MAGENTA: return ISO_BRIGHT( ISO_COLOR_MAGENTA );
758         case COLOR_LIGHT_YELLOW:  return ISO_BRIGHT( ISO_COLOR_YELLOW  );
759         case COLOR_WHITE:         return ISO_BRIGHT( ISO_COLOR_WHITE   );
760 
761         case COLOR_DEFAULT_FG:    return ISO_NORMAL( ISO_COLOR_DEFAULT );
762         case COLOR_DEFAULT_BG:    return ISO_NORMAL( ISO_COLOR_DEFAULT );
763         case COLOR_DEFAULT_LIGHT: return ISO_BRIGHT( ISO_COLOR_DEFAULT );
764 
765         default:                  return ISO_NORMAL( ISO_COLOR_DEFAULT );
766     }
767 }
768 
769 //////////////////////////////////////////////////////////////////////////////////////////
770 // Translate Herc color to ANSI/ISO-6429 (ECMA-48) SGR terminal escape sequence...
771 
set_screen_color(FILE * confp,short herc_fore,short herc_back)772 int set_screen_color( FILE* confp, short herc_fore, short herc_back )
773 {
774     uint16_t iso_fore, iso_back;
775     uint16_t  iso_bold_color, iso_dim_color;
776     int rc;
777 
778     // Translate Herc color to ANSI (ISO) color...
779 
780     iso_fore = ISO_COLOR( herc_fore );
781     iso_back = ISO_COLOR( herc_back );
782 
783     // PROGRAMMING NOTE: Because the only means we have to RELIABLY
784     // set non-bold (faint/dim) color attributes across ALL platforms
785     // is to use the '0' (reset) escape code (which of course has the
786     // unfortunate(?) side-effect of resetting BOTH the background
787     // AND foreground colors to dim/faint instead of just one or the
788     // other), we need to be careful to always set the dim (NON-bold)
789     // color attribute FIRST (which will of course reset both the fore-
790     // ground AND the backgound colors to non-bold/faint/dim as well),
791     // and then to, AFTERWARDS, set the BOLD color attribute. This is
792     // the ONLY way I've been able to discover (empirically via trial
793     // and error) how to RELIABLY set bold/faint foreground/background
794     // color attributes across all(?) supported platforms. (Fish)
795 
796     if ( ISO_IS_ISO_BRIGHT(iso_fore) == ISO_IS_ISO_BRIGHT(iso_back) )
797     {
798         // BOTH the foreground color AND the background colors
799         // are either BOTH bold or BOTH dim/faint (normal)...
800 
801         rc = fprintf
802         (
803             confp,
804 
805             // Set the bold/dim attribute FIRST and then
806             // BOTH foreground/background colors afterwards...
807 
808             "\x1B[%d;%d;%dm"
809 
810             ,ISO_NORMAL_OR_BRIGHT( iso_back )
811             ,ISO_BACKGROUND_COLOR( iso_back )
812             ,ISO_FOREGROUND_COLOR( iso_fore )
813         );
814     }
815     else // ( ISO_IS_ISO_BRIGHT(iso_fore) != ISO_IS_ISO_BRIGHT(iso_back) )
816     {
817         // ONE of either the foreground OR background colors
818         // is bold, but the OTHER one is dim/faint (normal)...
819 
820         if ( ISO_IS_ISO_BRIGHT(iso_fore) )
821         {
822             // The foregound color is the bright/bold one...
823 
824             iso_bold_color = ISO_FOREGROUND_COLOR( iso_fore );
825             iso_dim_color  = ISO_BACKGROUND_COLOR( iso_back );
826         }
827         else // ( !ISO_IS_ISO_BRIGHT(iso_fore) )
828         {
829             // The background color is the bright/bold one...
830 
831             iso_bold_color = ISO_BACKGROUND_COLOR( iso_back );
832             iso_dim_color  = ISO_FOREGROUND_COLOR( iso_fore );
833         }
834 
835         // Set whichever is the DIM color attribute FIRST
836         // and then AFTERWARDS whichever one is the BOLD...
837 
838         rc = fprintf
839         (
840             confp,
841 
842             "\x1B[0;%d;1;%dm" // (reset, dim-color, bold, bold-color)
843 
844             ,iso_dim_color
845             ,iso_bold_color
846         );
847     }
848 
849     return rc < 0 ? -1 : 0;
850 }
851 
852 //////////////////////////////////////////////////////////////////////////////////////////
853 
translate_keystroke(char kbbuf[],int * pkblen)854 void translate_keystroke( char kbbuf[], int* pkblen )
855 {
856     UNREFERENCED( kbbuf );
857     UNREFERENCED( pkblen );
858     return;
859 }
860 
861 //////////////////////////////////////////////////////////////////////////////////////////
862 
console_beep(FILE * confp)863 int console_beep( FILE* confp )
864 {
865     return fprintf( confp, "\a" ) < 0 ? -1 : 0;
866 }
867 
868 //////////////////////////////////////////////////////////////////////////////////////////
869 
get_console_dim(FILE * confp,int * rows,int * cols)870 int  get_console_dim( FILE* confp, int* rows, int* cols )
871 {
872     char* env;
873 #if defined(TIOCGWINSZ)
874     struct winsize winsize;
875 #else
876     UNREFERENCED( confp );
877 #endif
878 
879     if ( !rows || !cols )
880     {
881         errno = EINVAL;
882         return -1;
883     }
884 
885 #if defined(TIOCGWINSZ)
886     if (ioctl(fileno(confp), TIOCGWINSZ, &winsize) >= 0)
887     {
888         *rows = winsize.ws_row;
889         *cols = winsize.ws_col;
890     }
891     else
892 #endif
893     {
894         if (!(env = getenv( "LINES"   ))) *rows = 24;
895         else                              *rows = atoi(env);
896         if (!(env = getenv( "COLUMNS" ))) *cols = 80;
897         else                              *cols = atoi(env);
898     }
899 
900     if (!*rows || !*cols)
901     {
902         errno = EIO;
903         return -1;
904     }
905 
906     return 0;
907 }
908 
909 //////////////////////////////////////////////////////////////////////////////////////////
910 
911 #ifdef OPTION_EXTCURS
get_cursor_pos(int keybrd_fd,FILE * confp,short * row,short * col)912 int get_cursor_pos( int keybrd_fd, FILE* confp, short* row, short* col )
913 {
914     struct  timeval tv;                 /* Select timeout structure  */
915     fd_set  readset;                    /* Select file descriptors   */
916     char    kbbuf[16];                  /* Keyboard i/p buffer       */
917     char*   semi;                       /* Index of semicolon        */
918     int     kblen;                      /* Number of chars in kbbuf  */
919     int     maxfd;                      /* Highest file descriptor   */
920     int     rc;                         /* Return code               */
921     char    c;                          /* Work for scanf            */
922 
923     /* Request the CPR (Cursor Position Report) */
924     if ( fprintf( confp, KBD_ASK_CURSOR_POS ) < 0 )
925         return -1;
926 
927     /* Read the CPR from the keyboard i/p buffer */
928     while (1)
929     {
930         FD_ZERO (&readset);
931         FD_SET (keybrd_fd, &readset);
932         maxfd = keybrd_fd;
933         tv.tv_sec  = 0;
934         tv.tv_usec = 50 * 1000; // (PLENTY long enough!)
935 
936         /* Wait for CPR to arrive in our i/p buffer */
937         rc = select (maxfd + 1, &readset, NULL, NULL, &tv);
938 
939         if (rc < 0 )
940         {
941             if (errno == EINTR) continue;
942             errno = EIO;
943             break;
944         }
945 
946         /* If keyboard input has arrived then process it */
947         if (!FD_ISSET(keybrd_fd, &readset))
948             continue;
949 
950         /* Read character(s) from the keyboard */
951         kblen = read (keybrd_fd, kbbuf, sizeof(kbbuf)-1);
952 
953         if (kblen < 0)
954         {
955             errno = EIO;
956             break;
957         }
958 
959         kbbuf[kblen] = 0;
960 
961         // The returned CPR is the string "\x1B[n;mR"
962         // where n = decimal row, m = decimal column.
963 
964         // Note: we expect the entire the CPR to have
965         // been read on our first i/o (i.e. for it to
966         // have arrived all at once in one piece) and
967         // not piecemeal requiring several i/o's...
968         if (0
969             || kblen < 6
970             || kbbuf[   0   ] != '\x1B'
971             || kbbuf[   1   ] != '['
972             || kbbuf[kblen-1] != 'R'
973             || (semi = memchr( kbbuf, ';', kblen )) == NULL
974             || (semi - kbbuf) < 3
975             || sscanf( &kbbuf[2], "%hu%c", row, &c ) != 2 || c != ';'
976             || sscanf( semi+1,    "%hu%c", col, &c ) != 2 || c != 'R'
977         )
978         {
979             errno = EIO;
980             rc = -1;
981             break;
982         }
983 
984         /* Success! */
985         rc = 0;
986         break
987     }
988 
989     return rc;
990 }
991 #endif // OPTION_EXTCURS
992 
993 //////////////////////////////////////////////////////////////////////////////////////////
994 /*
995 From: (http://groups-beta.google.com/group/comp.protocols.kermit.misc/msg/1cc3ec6f0bfc0084)
996 
997 VGA-softcursor.txt, from the 2.2 kernel
998 
999 Software cursor for VGA    by Pavel Machek <p...@atrey.karlin.mff.cuni.cz>
1000 =======================    and Martin Mares <m...@atrey.karlin.mff.cuni.cz>
1001 
1002    Linux now has some ability to manipulate cursor appearance. Normally, you
1003 can set the size of hardware cursor (and also work around some ugly bugs in
1004 those miserable Trident cards--see #define TRIDENT_GLITCH in drivers/video/
1005 vgacon.c). You can now play a few new tricks:  you can make your cursor look
1006 like a non-blinking red block, make it inverse background of the character it's
1007 over or to highlight that character and still choose whether the original
1008 hardware cursor should remain visible or not.  There may be other things I have
1009 never thought of.
1010 
1011    The cursor appearance is controlled by a "<ESC>[?1;2;3c" escape sequence
1012 where 1, 2 and 3 are parameters described below. If you omit any of them,
1013 they will default to zeroes.
1014 
1015    Parameter 1 specifies cursor size (0=default, 1=invisible, 2=underline, ...,
1016 8=full block) + 16 if you want the software cursor to be applied + 32 if you
1017 want to always change the background color + 64 if you dislike having the
1018 background the same as the foreground.  Highlights are ignored for the last two
1019 flags.
1020 
1021    The second parameter selects character attribute bits you want to change
1022 (by simply XORing them with the value of this parameter). On standard VGA,
1023 the high four bits specify background and the low four the foreground. In both
1024 groups, low three bits set color (as in normal color codes used by the console)
1025 and the most significant one turns on highlight (or sometimes blinking--it
1026 depends on the configuration of your VGA).
1027 
1028    The third parameter consists of character attribute bits you want to set.
1029 Bit setting takes place before bit toggling, so you can simply clear a bit by
1030 including it in both the set mask and the toggle mask.
1031 
1032 Examples:
1033 =========
1034 
1035 To get normal blinking underline, use: echo -e '\033[?2c'
1036 To get blinking block, use:            echo -e '\033[?6c'
1037 To get red non-blinking block, use:    echo -e '\033[?17;0;64c'
1038 */
1039 
1040 int  set_console_cursor_shape( FILE* confp, int ins )
1041 {
1042 #if SET_CONSOLE_CURSOR_SHAPE_METHOD == CURSOR_SHAPE_NOT_SUPPORTED
1043 
1044     UNREFERENCED( confp );
1045     UNREFERENCED( ins );
1046     return 0;
1047 
1048 #elif SET_CONSOLE_CURSOR_SHAPE_METHOD == CURSOR_SHAPE_VIA_SPECIAL_LINUX_ESCAPE
1049 
1050 #define  LINUX_UNDER_BLINK_CURSOR    "\x1B[?2c"
1051 #define  LINUX_BLINK_BLOCK_CURSOR    "\x1B[?6c"
1052 
1053     return fprintf( confp, ins ? LINUX_UNDER_BLINK_CURSOR : LINUX_BLINK_BLOCK_CURSOR );
1054 
1055 #else
1056     #error Invalid #defined SET_CONSOLE_CURSOR_SHAPE_METHOD value
1057     return -1;
1058 #endif
1059 }
1060 
1061 //////////////////////////////////////////////////////////////////////////////////////////
1062 
1063 #endif // defined( WIN32 )
1064 
1065 //////////////////////////////////////////////////////////////////////////////////////////
1066