2
* Copyright (C) 1984-2000 Mark Nudelman
4
* You may distribute under the terms of either the GNU General Public
5
* License or the Less License, as specified in the README file.
7
* For more information about less, or for information on how to
8
* contact the author, see the README file.
13
* Routines which deal with the characteristics of the terminal.
14
* Uses termcap to be as terminal-independent as possible.
22
#if MSDOS_COMPILER==MSOFTC
25
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
27
#if MSDOS_COMPILER==DJGPPC
32
#if MSDOS_COMPILER==WIN32C
41
#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
43
#if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ)
44
#include <sys/ioctl.h>
55
#if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD))
56
#include <sys/ioctl.h>
68
#include <sys/signal.h>
72
#include <sys/stream.h>
78
#endif /* MSDOS_COMPILER */
81
* Check for broken termios package that forces you to manually
82
* set the line discipline.
85
#define MUST_SET_LINE_DISCIPLINE 1
87
#define MUST_SET_LINE_DISCIPLINE 0
91
#define DEFAULT_TERM "ansi"
92
static char *windowid;
94
#define DEFAULT_TERM "unknown"
97
#if MSDOS_COMPILER==MSOFTC
98
static int videopages;
99
static long msec_loops;
100
static int flash_created = 0;
101
#define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); }
104
#if MSDOS_COMPILER==BORLANDC
105
static unsigned short *whitescreen;
106
static int flash_created = 0;
108
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
109
#define _settextposition(y,x) gotoxy(x,y)
110
#define _clearscreen(m) clrscr()
111
#define _outtext(s) cputs(s)
112
#define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); }
113
extern int sc_height;
116
#if MSDOS_COMPILER==WIN32C
123
static int keyCount = 0;
124
static WORD curr_attr;
125
static int pending_scancode = 0;
126
static WORD *whitescreen;
128
static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */
129
static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */
130
HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */
133
static void win32_init_term();
134
static void win32_deinit_term();
136
#define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
137
#define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY)
138
#define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4)))
139
#define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \
140
if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \
141
error("SETCOLORS failed"); }
145
public int nm_fg_color; /* Color of normal text */
146
public int nm_bg_color;
147
public int bo_fg_color; /* Color of bold text */
148
public int bo_bg_color;
149
public int ul_fg_color; /* Color of underlined text */
150
public int ul_bg_color;
151
public int so_fg_color; /* Color of standout text */
152
public int so_bg_color;
153
public int bl_fg_color; /* Color of blinking text */
154
public int bl_bg_color;
155
static int sy_fg_color; /* Color of system text (before less) */
156
static int sy_bg_color;
161
* Strings passed to tputs() to do various terminal functions.
164
*sc_pad, /* Pad string */
165
*sc_home, /* Cursor home */
166
*sc_addline, /* Add line, scroll down following lines */
167
*sc_lower_left, /* Cursor to last line, first column */
168
*sc_move, /* General cursor positioning */
169
*sc_clear, /* Clear screen */
170
*sc_eol_clear, /* Clear to end of line */
171
*sc_eos_clear, /* Clear to end of screen */
172
*sc_s_in, /* Enter standout (highlighted) mode */
173
*sc_s_out, /* Exit standout mode */
174
*sc_u_in, /* Enter underline mode */
175
*sc_u_out, /* Exit underline mode */
176
*sc_b_in, /* Enter bold mode */
177
*sc_b_out, /* Exit bold mode */
178
*sc_bl_in, /* Enter blink mode */
179
*sc_bl_out, /* Exit blink mode */
180
*sc_visual_bell, /* Visual bell (flash screen) sequence */
181
*sc_backspace, /* Backspace cursor */
182
*sc_s_keypad, /* Start keypad mode */
183
*sc_e_keypad, /* End keypad mode */
184
*sc_init, /* Startup terminal initialization */
185
*sc_deinit; /* Exit terminal de-initialization */
188
static int init_done = 0;
190
public int auto_wrap; /* Terminal does \r\n when write past margin */
191
public int ignaw; /* Terminal ignores \n immediately after wrap */
192
public int erase_char, kill_char; /* The user's erase and line-kill chars */
193
public int werase_char; /* The user's word-erase char */
194
public int sc_width, sc_height; /* Height & width of screen */
195
public int bo_s_width, bo_e_width; /* Printing width of boldface seq */
196
public int ul_s_width, ul_e_width; /* Printing width of underline seq */
197
public int so_s_width, so_e_width; /* Printing width of standout seq */
198
public int bl_s_width, bl_e_width; /* Printing width of blink seq */
199
public int above_mem, below_mem; /* Memory retained above/below screen */
200
public int can_goto_line; /* Can move cursor to any line */
201
public int clear_bg; /* Clear fills with background color */
202
public int missing_cap = 0; /* Some capability is missing */
204
static int attrmode = AT_NORMAL;
207
static char *cheaper();
208
static void tmodes();
212
* These two variables are sometimes defined in,
213
* and needed by, the termcap library.
215
#if MUST_DEFINE_OSPEED
216
extern short ospeed; /* Terminal output baud rate */
217
extern char PC; /* Pad character */
224
extern int quiet; /* If VERY_QUIET, use visual bell for bell */
225
extern int no_back_scroll;
228
extern int no_keypad;
231
extern int screen_trashed;
233
extern int hilite_search;
236
extern char *tgetstr();
237
extern char *tgoto();
241
* Change terminal to "raw mode", or restore to "normal" mode.
243
* 1. An outstanding read will complete on receipt of a single keystroke.
244
* 2. Input is not echoed.
245
* 3. On output, \n is mapped to \r\n.
246
* 4. \t is NOT expanded into spaces.
247
* 5. Signal-causing characters such as ctrl-C (interrupt),
248
* etc. are NOT disabled.
249
* It doesn't matter whether an input \n is mapped to \r, or vice versa.
255
static int curr_on = 0;
259
#if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS
262
static struct termios save_term;
263
static int saved_term = 0;
268
* Get terminal modes.
273
* Save modes and set certain variables dependent on modes.
281
switch (cfgetospeed(&s))
284
case B0: ospeed = 0; break;
287
case B50: ospeed = 1; break;
290
case B75: ospeed = 2; break;
293
case B110: ospeed = 3; break;
296
case B134: ospeed = 4; break;
299
case B150: ospeed = 5; break;
302
case B200: ospeed = 6; break;
305
case B300: ospeed = 7; break;
308
case B600: ospeed = 8; break;
311
case B1200: ospeed = 9; break;
314
case B1800: ospeed = 10; break;
317
case B2400: ospeed = 11; break;
320
case B4800: ospeed = 12; break;
323
case B9600: ospeed = 13; break;
326
case EXTA: ospeed = 14; break;
329
case EXTB: ospeed = 15; break;
332
case B57600: ospeed = 16; break;
335
case B115200: ospeed = 17; break;
340
erase_char = s.c_cc[VERASE];
341
kill_char = s.c_cc[VKILL];
343
werase_char = s.c_cc[VWERASE];
345
werase_char = CONTROL('W');
349
* Set the modes to the way we want them.
411
#if MUST_SET_LINE_DISCIPLINE
413
* System's termios is broken; need to explicitly
414
* request TERMIODISC line discipline.
416
s.c_line = TERMIODISC;
421
* Restore saved modes.
428
tcsetattr(2, TCSADRAIN, &s);
429
#if MUST_SET_LINE_DISCIPLINE
433
* Broken termios *ignores* any line discipline
434
* except TERMIODISC. A different old line discipline
435
* is therefore not restored, yet. Restore the old
436
* line discipline by hand.
438
ioctl(2, TIOCSETD, &save_term.c_line);
446
static struct termio save_term;
447
static int saved_term = 0;
452
* Get terminal modes.
454
ioctl(2, TCGETA, &s);
457
* Save modes and set certain variables dependent on modes.
465
ospeed = s.c_cflag & CBAUD;
467
erase_char = s.c_cc[VERASE];
468
kill_char = s.c_cc[VKILL];
470
werase_char = s.c_cc[VWERASE];
472
werase_char = CONTROL('W');
476
* Set the modes to the way we want them.
478
s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
479
s.c_oflag |= (OPOST|ONLCR|TAB3);
480
s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
486
* Restore saved modes.
490
ioctl(2, TCSETAW, &s);
496
static struct sgttyb save_term;
497
static int saved_term = 0;
502
* Get terminal modes.
504
ioctl(2, TIOCGETP, &s);
507
* Save modes and set certain variables dependent on modes.
515
ospeed = s.sg_ospeed;
517
erase_char = s.sg_erase;
518
kill_char = s.sg_kill;
519
werase_char = CONTROL('W');
522
* Set the modes to the way we want them.
524
s.sg_flags |= CBREAK;
525
s.sg_flags &= ~(ECHO|XTABS);
529
* Restore saved modes.
533
ioctl(2, TIOCSETN, &s);
539
static struct sgbuf save_term;
540
static int saved_term = 0;
545
* Get terminal modes.
550
* Save modes and set certain variables dependent on modes.
557
erase_char = s.sg_bspch;
558
kill_char = s.sg_dlnch;
559
werase_char = CONTROL('W');
562
* Set the modes to the way we want them.
571
* Restore saved modes.
578
/* MS-DOS, Windows, or OS2 */
581
LSIGNAL(SIGINT, SIG_IGN);
584
#if MSDOS_COMPILER==DJGPPC
585
kill_char = CONTROL('U');
587
* So that when we shell out or run another program, its
588
* stdin is in cooked mode. We do not switch stdin to binary
589
* mode if fd0 is zero, since that means we were called before
590
* tty was reopened in open_getchr, in which case we would be
591
* changing the original stdin device outside less.
594
setmode(0, on ? O_BINARY : O_TEXT);
598
werase_char = CONTROL('W');
608
* Some glue to prevent calling termcap functions if tgetent() failed.
618
strcpy(name, "LESS_TERMCAP_");
619
strcat(name, capname);
620
return (lgetenv(name));
629
if ((s = ltget_env(capname)) != NULL)
630
return (*s != '\0' && *s != '0');
633
return (tgetflag(capname));
642
if ((s = ltget_env(capname)) != NULL)
646
return (tgetnum(capname));
650
ltgetstr(capname, pp)
656
if ((s = ltget_env(capname)) != NULL)
660
return (tgetstr(capname, pp));
662
#endif /* MSDOS_COMPILER */
665
* Get size of the output screen.
677
#define DEF_SC_WIDTH 80
679
#define DEF_SC_HEIGHT 25
681
#define DEF_SC_HEIGHT 24
685
sys_width = sys_height = 0;
687
#if MSDOS_COMPILER==MSOFTC
689
struct videoconfig w;
691
sys_height = w.numtextrows;
692
sys_width = w.numtextcols;
695
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
699
sys_height = w.screenheight;
700
sys_width = w.screenwidth;
703
#if MSDOS_COMPILER==WIN32C
705
CONSOLE_SCREEN_BUFFER_INFO scr;
706
GetConsoleScreenBufferInfo(con_out, &scr);
707
sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1;
708
sys_width = scr.srWindow.Right - scr.srWindow.Left + 1;
718
* When using terminal emulators for XFree86/OS2, the
719
* _scrsize function does not work well.
720
* Call the scrsize.exe program to get the window size.
722
windowid = getenv("WINDOWID");
723
if (windowid != NULL)
725
FILE *fd = popen("scrsize", "rt");
729
fscanf(fd, "%i %i", &w, &h);
743
if (ioctl(2, TIOCGWINSZ, &w) == 0)
746
sys_height = w.ws_row;
748
sys_width = w.ws_col;
755
if (ioctl(2, WIOCGETD, &w) == 0)
758
sys_height = w.uw_height / w.uw_vs;
760
sys_width = w.uw_width / w.uw_hs;
771
sc_height = sys_height;
772
else if ((s = lgetenv("LINES")) != NULL)
775
else if ((n = ltgetnum("li")) > 0)
779
sc_height = DEF_SC_HEIGHT;
782
sc_width = sys_width;
783
else if ((s = lgetenv("COLUMNS")) != NULL)
786
else if ((n = ltgetnum("co")) > 0)
790
sc_width = DEF_SC_WIDTH;
793
#if MSDOS_COMPILER==MSOFTC
795
* Figure out how many empty loops it takes to delay a millisecond.
803
* Get synchronized at the start of a tick.
806
while (clock() == start)
809
* Now count loops till the next tick.
813
while (clock() == start)
816
* Convert from (loops per clock) to (loops per millisecond).
818
msec_loops *= CLOCKS_PER_SEC;
823
* Delay for a specified number of milliseconds.
828
static long delay_dummy = 0;
840
for (i = 0; i < msec_loops; i++)
843
* Make it look like we're doing something here,
844
* so the optimizer doesn't remove the whole loop.
853
* Return the characters actually input by a "special" key.
859
static char tbuf[40];
861
#if MSDOS_COMPILER || OS2
862
static char k_right[] = { '\340', PCK_RIGHT, 0 };
863
static char k_left[] = { '\340', PCK_LEFT, 0 };
864
static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 };
865
static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 };
866
static char k_insert[] = { '\340', PCK_INSERT, 0 };
867
static char k_delete[] = { '\340', PCK_DELETE, 0 };
868
static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 };
869
static char k_ctl_backspace[] = { '\177', 0 };
870
static char k_home[] = { '\340', PCK_HOME, 0 };
871
static char k_end[] = { '\340', PCK_END, 0 };
872
static char k_up[] = { '\340', PCK_UP, 0 };
873
static char k_down[] = { '\340', PCK_DOWN, 0 };
874
static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 };
875
static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 };
876
static char k_pageup[] = { '\340', PCK_PAGEUP, 0 };
877
static char k_f1[] = { '\340', PCK_F1, 0 };
887
* If windowid is not NULL, assume less is executed in
888
* the XFree86 environment.
891
s = windowid ? ltgetstr("kr", &sp) : k_right;
894
s = windowid ? ltgetstr("kl", &sp) : k_left;
897
s = windowid ? ltgetstr("ku", &sp) : k_up;
900
s = windowid ? ltgetstr("kd", &sp) : k_down;
903
s = windowid ? ltgetstr("kP", &sp) : k_pageup;
906
s = windowid ? ltgetstr("kN", &sp) : k_pagedown;
909
s = windowid ? ltgetstr("kh", &sp) : k_home;
912
s = windowid ? ltgetstr("@7", &sp) : k_end;
917
s = ltgetstr("kD", &sp);
957
#if MSDOS_COMPILER || OS2
961
case SK_CTL_LEFT_ARROW:
964
case SK_CTL_RIGHT_ARROW:
967
case SK_CTL_BACKSPACE:
981
s = ltgetstr("kr", &sp);
984
s = ltgetstr("kl", &sp);
987
s = ltgetstr("ku", &sp);
990
s = ltgetstr("kd", &sp);
993
s = ltgetstr("kP", &sp);
996
s = ltgetstr("kN", &sp);
999
s = ltgetstr("kh", &sp);
1002
s = ltgetstr("@7", &sp);
1005
s = ltgetstr("kD", &sp);
1015
tbuf[0] = CONTROL('K');
1026
* Get terminal capabilities via termcap.
1037
* Set up default colors.
1038
* The xx_s_width and xx_e_width vars are already initialized to 0.
1040
#if MSDOS_COMPILER==MSOFTC
1041
sy_bg_color = _getbkcolor();
1042
sy_fg_color = _gettextcolor();
1045
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1049
sy_bg_color = (w.attribute >> 4) & 0x0F;
1050
sy_fg_color = (w.attribute >> 0) & 0x0F;
1053
#if MSDOS_COMPILER==WIN32C
1056
CONSOLE_SCREEN_BUFFER_INFO scr;
1058
con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE);
1060
* Always open stdin in binary. Note this *must* be done
1061
* before any file operations have been done on fd0.
1064
GetConsoleScreenBufferInfo(con_out, &scr);
1065
ReadConsoleOutputAttribute(con_out, &curr_attr,
1066
1, scr.dwCursorPosition, &nread);
1067
sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */
1068
sy_fg_color = curr_attr & FG_COLORS;
1073
nm_fg_color = sy_fg_color;
1074
nm_bg_color = sy_bg_color;
1085
* Get size of the screen.
1091
#else /* !MSDOS_COMPILER */
1094
register char *t1, *t2;
1096
char termbuf[TERMBUF_SIZE];
1098
static char sbuf[TERMSBUF_SIZE];
1102
* Make sure the termcap database is available.
1104
sp = lgetenv("TERMCAP");
1105
if (sp == NULL || *sp == '\0')
1108
if ((sp = homefile("termcap.dat")) != NULL)
1110
termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char));
1111
sprintf(termcap, "TERMCAP=%s", sp);
1118
* Find out what kind of terminal this is.
1120
if ((term = lgetenv("TERM")) == NULL)
1121
term = DEFAULT_TERM;
1123
if (tgetent(termbuf, term) <= 0)
1125
if (ltgetflag("hc"))
1129
* Get size of the screen.
1134
auto_wrap = ltgetflag("am");
1135
ignaw = ltgetflag("xn");
1136
above_mem = ltgetflag("da");
1137
below_mem = ltgetflag("db");
1138
clear_bg = ltgetflag("ut");
1141
* Assumes termcap variable "sg" is the printing width of:
1142
* the standout sequence, the end standout sequence,
1143
* the underline sequence, the end underline sequence,
1144
* the boldface sequence, and the end boldface sequence.
1146
if ((so_s_width = ltgetnum("sg")) < 0)
1148
so_e_width = so_s_width;
1150
bo_s_width = bo_e_width = so_s_width;
1151
ul_s_width = ul_e_width = so_s_width;
1152
bl_s_width = bl_e_width = so_s_width;
1155
if (so_s_width > 0 || so_e_width > 0)
1157
* Disable highlighting by default on magic cookie terminals.
1158
* Turning on highlighting might change the displayed width
1159
* of a line, causing the display to get messed up.
1160
* The user can turn it back on with -g,
1161
* but she won't like the results.
1167
* Get various string-valued capabilities.
1172
sc_pad = ltgetstr("pc", &sp);
1177
sc_s_keypad = ltgetstr("ks", &sp);
1178
if (sc_s_keypad == NULL)
1180
sc_e_keypad = ltgetstr("ke", &sp);
1181
if (sc_e_keypad == NULL)
1184
sc_init = ltgetstr("ti", &sp);
1185
if (sc_init == NULL)
1188
sc_deinit= ltgetstr("te", &sp);
1189
if (sc_deinit == NULL)
1192
sc_eol_clear = ltgetstr("ce", &sp);
1193
if (sc_eol_clear == NULL || *sc_eol_clear == '\0')
1199
sc_eos_clear = ltgetstr("cd", &sp);
1200
if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0'))
1206
sc_clear = ltgetstr("cl", &sp);
1207
if (sc_clear == NULL || *sc_clear == '\0')
1213
sc_move = ltgetstr("cm", &sp);
1214
if (sc_move == NULL || *sc_move == '\0')
1217
* This is not an error here, because we don't
1218
* always need sc_move.
1219
* We need it only if we don't have home or lower-left.
1226
tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp);
1227
tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp);
1228
tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp);
1229
tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp);
1231
sc_visual_bell = ltgetstr("vb", &sp);
1232
if (sc_visual_bell == NULL)
1233
sc_visual_bell = "";
1235
if (ltgetflag("bs"))
1236
sc_backspace = "\b";
1239
sc_backspace = ltgetstr("bc", &sp);
1240
if (sc_backspace == NULL || *sc_backspace == '\0')
1241
sc_backspace = "\b";
1245
* Choose between using "ho" and "cm" ("home" and "cursor move")
1246
* to move the cursor to the upper left corner of the screen.
1248
t1 = ltgetstr("ho", &sp);
1251
if (*sc_move == '\0')
1255
strcpy(sp, tgoto(sc_move, 0, 0));
1257
sp += strlen(sp) + 1;
1259
sc_home = cheaper(t1, t2, "|\b^");
1262
* Choose between using "ll" and "cm" ("lower left" and "cursor move")
1263
* to move the cursor to the lower left corner of the screen.
1265
t1 = ltgetstr("ll", &sp);
1268
if (*sc_move == '\0')
1272
strcpy(sp, tgoto(sc_move, 0, sc_height-1));
1274
sp += strlen(sp) + 1;
1276
sc_lower_left = cheaper(t1, t2, "\r");
1279
* Choose between using "al" or "sr" ("add line" or "scroll reverse")
1280
* to add a line at the top of the screen.
1282
t1 = ltgetstr("al", &sp);
1285
t2 = ltgetstr("sr", &sp);
1289
if (*t1 == '\0' && *t2 == '\0')
1296
sc_addline = cheaper(t1, t2, "");
1297
if (*sc_addline == '\0')
1300
* Force repaint on any backward movement.
1304
#endif /* MSDOS_COMPILER */
1309
* Return the cost of displaying a termcap string.
1310
* We use the trick of calling tputs, but as a char printing function
1311
* we give it inc_costcount, which just increments "costcount".
1312
* This tells us how many chars would be printed by using this string.
1313
* {{ Couldn't we just use strlen? }}
1315
static int costcount;
1331
tputs(t, sc_height, inc_costcount);
1336
* Return the "best" of the two given termcap strings.
1337
* The best, if both exist, is the one with the lower
1338
* cost (see cost() function).
1341
cheaper(t1, t2, def)
1345
if (*t1 == '\0' && *t2 == '\0')
1354
if (cost(t1) < cost(t2))
1360
tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp)
1369
*instr = ltgetstr(incap, spp);
1374
*outstr = def_outstr;
1378
*outstr = ltgetstr(outcap, spp);
1379
if (*outstr == NULL)
1380
/* No specific out capability; use "me". */
1381
*outstr = ltgetstr("me", spp);
1382
if (*outstr == NULL)
1383
/* Don't even have "me"; use a null string. */
1387
#endif /* MSDOS_COMPILER */
1391
* Below are the functions which perform all the
1392
* terminal-specific screen manipulation.
1398
#if MSDOS_COMPILER==WIN32C
1400
_settextposition(int row, int col)
1403
CONSOLE_SCREEN_BUFFER_INFO csbi;
1405
GetConsoleScreenBufferInfo(con_out, &csbi);
1406
cpos.X = csbi.srWindow.Left + (col - 1);
1407
cpos.Y = csbi.srWindow.Top + (row - 1);
1408
SetConsoleCursorPosition(con_out, cpos);
1413
* Initialize the screen to the correct color at startup.
1418
SETCOLORS(nm_fg_color, nm_bg_color);
1421
* This clears the screen at startup. This is different from
1422
* the behavior of other versions of less. Disable it for now.
1429
* Create a complete, blank screen using "normal" colors.
1431
SETCOLORS(nm_fg_color, nm_bg_color);
1432
blanks = (char *) ecalloc(width+1, sizeof(char));
1433
for (col = 0; col < sc_width; col++)
1435
blanks[sc_width] = '\0';
1436
for (row = 0; row < sc_height; row++)
1443
#if MSDOS_COMPILER==WIN32C
1446
* Termcap-like init with a private win32 console.
1451
CONSOLE_SCREEN_BUFFER_INFO scr;
1454
if (con_out_save == INVALID_HANDLE_VALUE)
1457
GetConsoleScreenBufferInfo(con_out_save, &scr);
1459
if (con_out_ours == INVALID_HANDLE_VALUE)
1462
* Create our own screen buffer, so that we
1463
* may restore the original when done.
1465
con_out_ours = CreateConsoleScreenBuffer(
1466
GENERIC_WRITE | GENERIC_READ,
1467
FILE_SHARE_WRITE | FILE_SHARE_READ,
1468
(LPSECURITY_ATTRIBUTES) NULL,
1469
CONSOLE_TEXTMODE_BUFFER,
1473
size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1474
size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1475
SetConsoleScreenBufferSize(con_out_ours, size);
1476
SetConsoleActiveScreenBuffer(con_out_ours);
1477
con_out = con_out_ours;
1481
* Restore the startup console.
1486
if (con_out_save == INVALID_HANDLE_VALUE)
1489
(void) CloseHandle(con_out_ours);
1490
SetConsoleActiveScreenBuffer(con_out_save);
1491
con_out = con_out_save;
1497
* Initialize terminal
1504
tputs(sc_init, sc_height, putchr);
1506
tputs(sc_s_keypad, sc_height, putchr);
1508
#if MSDOS_COMPILER==WIN32C
1519
* Deinitialize terminal
1528
tputs(sc_e_keypad, sc_height, putchr);
1530
tputs(sc_deinit, sc_height, putchr);
1532
/* Restore system colors. */
1533
SETCOLORS(sy_fg_color, sy_bg_color);
1534
#if MSDOS_COMPILER==WIN32C
1536
win32_deinit_term();
1538
/* Need clreol to make SETCOLORS take effect. */
1546
* Home cursor (move to upper left corner of screen).
1552
tputs(sc_home, 1, putchr);
1555
_settextposition(1,1);
1560
* Add a blank line (called with cursor at home).
1561
* Should scroll the display down.
1567
tputs(sc_addline, sc_height, putchr);
1570
#if MSDOS_COMPILER==MSOFTC
1571
_scrolltextwindow(_GSCROLLDOWN);
1572
_settextposition(1,1);
1574
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1575
movetext(1,1, sc_width,sc_height-1, 1,2);
1579
#if MSDOS_COMPILER==WIN32C
1582
SMALL_RECT rcSrc, rcClip;
1584
CONSOLE_SCREEN_BUFFER_INFO csbi;
1586
GetConsoleScreenBufferInfo(con_out,&csbi);
1588
/* The clip rectangle is the entire visible screen. */
1589
rcClip.Left = csbi.srWindow.Left;
1590
rcClip.Top = csbi.srWindow.Top;
1591
rcClip.Right = csbi.srWindow.Right;
1592
rcClip.Bottom = csbi.srWindow.Bottom;
1594
/* The source rectangle is the visible screen minus the last line. */
1598
/* Move the top left corner of the source window down one row. */
1599
new_org.X = rcSrc.Left;
1600
new_org.Y = rcSrc.Top + 1;
1602
/* Fill the right character and attributes. */
1603
fillchar.Char.AsciiChar = ' ';
1604
curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1605
fillchar.Attributes = curr_attr;
1606
ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1607
_settextposition(1,1);
1617
* Remove the n topmost lines and scroll everything below it in the
1618
* window upward. This is needed to stop leaking the topmost line
1619
* into the scrollback buffer when we go down-one-line (in WIN32).
1625
#if MSDOS_COMPILER==WIN32C
1626
SMALL_RECT rcSrc, rcClip;
1629
CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */
1631
if (n >= sc_height - 1)
1640
GetConsoleScreenBufferInfo(con_out, &csbi);
1642
/* Get the extent of all-visible-rows-but-the-last. */
1643
rcSrc.Left = csbi.srWindow.Left;
1644
rcSrc.Top = csbi.srWindow.Top + n;
1645
rcSrc.Right = csbi.srWindow.Right;
1646
rcSrc.Bottom = csbi.srWindow.Bottom;
1648
/* Get the clip rectangle. */
1649
rcClip.Left = rcSrc.Left;
1650
rcClip.Top = csbi.srWindow.Top;
1651
rcClip.Right = rcSrc.Right;
1652
rcClip.Bottom = rcSrc.Bottom ;
1654
/* Move the source window up n rows. */
1655
new_org.X = rcSrc.Left;
1656
new_org.Y = rcSrc.Top - n;
1658
/* Fill the right character and attributes. */
1659
fillchar.Char.AsciiChar = ' ';
1660
curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1661
fillchar.Attributes = curr_attr;
1663
ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1665
/* Position cursor on first blank line. */
1666
goto_line(sc_height - n - 1);
1671
#if MSDOS_COMPILER==WIN32C
1679
* This will clear only the currently visible rows of the NT
1680
* console buffer, which means none of the precious scrollback
1681
* rows are touched making for faster scrolling. Note that, if
1682
* the window has fewer columns than the console buffer (i.e.
1683
* there is a horizontal scrollbar as well), the entire width
1684
* of the visible rows will be cleared.
1689
CONSOLE_SCREEN_BUFFER_INFO csbi;
1691
/* get the number of cells in the current buffer */
1692
GetConsoleScreenBufferInfo(con_out, &csbi);
1693
winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
1695
topleft.Y = csbi.srWindow.Top;
1697
curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
1698
FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars);
1699
FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars);
1703
* Remove the n topmost lines and scroll everything below it in the
1710
SMALL_RECT rcSrc, rcClip;
1716
CONSOLE_SCREEN_BUFFER_INFO csbi;
1721
if (n >= sc_height - 1)
1724
_settextposition(1,1);
1728
/* Get the extent of what will remain visible after scrolling. */
1729
GetConsoleScreenBufferInfo(con_out, &csbi);
1730
rcSrc.Left = csbi.srWindow.Left;
1731
rcSrc.Top = csbi.srWindow.Top + n;
1732
rcSrc.Right = csbi.srWindow.Right;
1733
rcSrc.Bottom = csbi.srWindow.Bottom;
1735
/* Get the clip rectangle. */
1736
rcClip.Left = rcSrc.Left;
1737
rcClip.Top = csbi.srWindow.Top;
1738
rcClip.Right = rcSrc.Right;
1739
rcClip.Bottom = rcSrc.Bottom ;
1741
/* Move the source text to the top of the screen. */
1742
new_org.X = rcSrc.Left;
1745
/* Fill the right character and attributes. */
1746
fillchar.Char.AsciiChar = ' ';
1747
fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color);
1749
/* Scroll the window. */
1750
SetConsoleTextAttribute(con_out, fillchar.Attributes);
1751
ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar);
1753
/* Clear remaining lines at bottom. */
1754
topleft.X = csbi.dwCursorPosition.X;
1755
topleft.Y = rcSrc.Bottom - n;
1756
size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X);
1757
FillConsoleOutputCharacter(con_out, ' ', size, topleft,
1759
FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft,
1761
SetConsoleTextAttribute(con_out, curr_attr);
1763
/* Move cursor n lines up from where it was. */
1764
csbi.dwCursorPosition.Y -= n;
1765
SetConsoleCursorPosition(con_out, csbi.dwCursorPosition);
1770
* Move cursor to lower left corner of screen.
1776
tputs(sc_lower_left, 1, putchr);
1779
_settextposition(sc_height, 1);
1784
* Check if the console size has changed and reset internals
1785
* (in lieu of SIGWINCH for WIN32).
1790
#if MSDOS_COMPILER==WIN32C
1791
CONSOLE_SCREEN_BUFFER_INFO scr;
1794
if (con_out == INVALID_HANDLE_VALUE)
1798
GetConsoleScreenBufferInfo(con_out, &scr);
1799
size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1;
1800
size.X = scr.srWindow.Right - scr.srWindow.Left + 1;
1801
if (size.Y != sc_height || size.X != sc_width)
1805
if (!no_init && con_out_ours == con_out)
1806
SetConsoleScreenBufferSize(con_out, size);
1808
wscroll = (sc_height + 1) / 2;
1815
* Goto a specific line on the screen.
1822
tputs(tgoto(sc_move, 0, slinenum), 1, putchr);
1825
_settextposition(slinenum+1, 1);
1829
#if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC
1831
* Create an alternate screen which is all white.
1832
* This screen is used to create a "flash" effect, by displaying it
1833
* briefly and then switching back to the normal screen.
1834
* {{ Yuck! There must be a better way to get a visual bell. }}
1839
#if MSDOS_COMPILER==MSOFTC
1840
struct videoconfig w;
1844
_getvideoconfig(&w);
1845
videopages = w.numvideopages;
1854
blanks = (char *) ecalloc(w.numtextcols, sizeof(char));
1855
for (col = 0; col < w.numtextcols; col++)
1857
for (row = w.numtextrows; row > 0; row--)
1858
_outmem(blanks, w.numtextcols);
1865
#if MSDOS_COMPILER==BORLANDC
1868
whitescreen = (unsigned short *)
1869
malloc(sc_width * sc_height * sizeof(short));
1870
if (whitescreen == NULL)
1872
for (n = 0; n < sc_width * sc_height; n++)
1873
whitescreen[n] = 0x7020;
1875
#if MSDOS_COMPILER==WIN32C
1878
whitescreen = (WORD *)
1879
malloc(sc_height * sc_width * sizeof(WORD));
1880
if (whitescreen == NULL)
1882
/* Invert the standard colors. */
1883
for (n = 0; n < sc_width * sc_height; n++)
1884
whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color);
1890
#endif /* MSDOS_COMPILER */
1893
* Output the "visual bell", if there is one.
1899
if (*sc_visual_bell == '\0')
1901
tputs(sc_visual_bell, sc_height, putchr);
1903
#if MSDOS_COMPILER==DJGPPC
1906
#if MSDOS_COMPILER==MSOFTC
1908
* Create a flash screen on the second video page.
1909
* Switch to that page, then switch back.
1919
#if MSDOS_COMPILER==BORLANDC
1920
unsigned short *currscreen;
1923
* Get a copy of the current screen.
1924
* Display the flash screen.
1925
* Then restore the old screen.
1929
if (whitescreen == NULL)
1931
currscreen = (unsigned short *)
1932
malloc(sc_width * sc_height * sizeof(short));
1933
if (currscreen == NULL) return;
1934
gettext(1, 1, sc_width, sc_height, currscreen);
1935
puttext(1, 1, sc_width, sc_height, whitescreen);
1937
puttext(1, 1, sc_width, sc_height, currscreen);
1940
#if MSDOS_COMPILER==WIN32C
1941
/* paint screen with an inverse color */
1944
/* leave it displayed for 100 msec. */
1947
/* restore with a redraw */
1963
putchr(CONTROL('G'));
1965
#if MSDOS_COMPILER==WIN32C
1974
* Ring the terminal bell.
1979
if (quiet == VERY_QUIET)
1992
tputs(sc_clear, sc_height, putchr);
1995
#if MSDOS_COMPILER==WIN32C
1998
_clearscreen(_GCLEARSCREEN);
2004
* Clear from the cursor to the end of the cursor's line.
2005
* {{ This must not move the cursor. }}
2011
tputs(sc_eol_clear, 1, putchr);
2013
#if MSDOS_COMPILER==MSOFTC
2016
struct rccoord tpos;
2020
* Save current state.
2022
tpos = _gettextposition();
2023
_gettextwindow(&top, &left, &bot, &right);
2025
* Set a temporary window to the current line,
2026
* from the cursor's position to the right edge of the screen.
2027
* Then clear that window.
2029
_settextwindow(tpos.row, tpos.col, tpos.row, sc_width);
2030
_clearscreen(_GWINDOW);
2034
_settextwindow(top, left, bot, right);
2035
_settextposition(tpos.row, tpos.col);
2037
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2041
#if MSDOS_COMPILER==WIN32C
2044
CONSOLE_SCREEN_BUFFER_INFO scr;
2047
memset(&scr, 0, sizeof(scr));
2048
GetConsoleScreenBufferInfo(con_out, &scr);
2049
cpos.X = scr.dwCursorPosition.X;
2050
cpos.Y = scr.dwCursorPosition.Y;
2051
curr_attr = MAKEATTR(nm_fg_color, nm_bg_color);
2052
FillConsoleOutputAttribute(con_out, curr_attr,
2053
scr.dwSize.X - cpos.X, cpos, &nchars);
2054
FillConsoleOutputCharacter(con_out, ' ',
2055
scr.dwSize.X - cpos.X, cpos, &nchars);
2063
* Clear the current line.
2064
* Clear the screen if there's off-screen memory below the display.
2073
tputs(sc_eos_clear, 1, putchr);
2075
tputs(sc_eol_clear, 1, putchr);
2080
* Clear the bottom line of the display.
2081
* Leave the cursor at the beginning of the bottom line.
2087
* If we're in a non-normal attribute mode, temporarily exit
2088
* the mode while we do the clear. Some terminals fill the
2089
* cleared area with the current attribute.
2121
* Begin "standout" (bold, underline, or whatever).
2127
tputs(sc_s_in, 1, putchr);
2130
SETCOLORS(so_fg_color, so_bg_color);
2132
attrmode = AT_STANDOUT;
2142
tputs(sc_s_out, 1, putchr);
2145
SETCOLORS(nm_fg_color, nm_bg_color);
2147
attrmode = AT_NORMAL;
2151
* Begin "underline" (hopefully real underlining,
2152
* otherwise whatever the terminal provides).
2158
tputs(sc_u_in, 1, putchr);
2161
SETCOLORS(ul_fg_color, ul_bg_color);
2163
attrmode = AT_UNDERLINE;
2173
tputs(sc_u_out, 1, putchr);
2176
SETCOLORS(nm_fg_color, nm_bg_color);
2178
attrmode = AT_NORMAL;
2188
tputs(sc_b_in, 1, putchr);
2191
SETCOLORS(bo_fg_color, bo_bg_color);
2203
tputs(sc_b_out, 1, putchr);
2206
SETCOLORS(nm_fg_color, nm_bg_color);
2208
attrmode = AT_NORMAL;
2218
tputs(sc_bl_in, 1, putchr);
2221
SETCOLORS(bl_fg_color, bl_bg_color);
2223
attrmode = AT_BLINK;
2233
tputs(sc_bl_out, 1, putchr);
2236
SETCOLORS(nm_fg_color, nm_bg_color);
2238
attrmode = AT_NORMAL;
2241
#if 0 /* No longer used */
2243
* Erase the character to the left of the cursor
2244
* and move the cursor left.
2251
* Erase the previous character by overstriking with a space.
2253
tputs(sc_backspace, 1, putchr);
2255
tputs(sc_backspace, 1, putchr);
2257
#if MSDOS_COMPILER==MSOFTC
2258
struct rccoord tpos;
2261
tpos = _gettextposition();
2264
_settextposition(tpos.row, tpos.col-1);
2266
_settextposition(tpos.row, tpos.col-1);
2268
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2271
#if MSDOS_COMPILER==WIN32C
2274
CONSOLE_SCREEN_BUFFER_INFO scr;
2277
GetConsoleScreenBufferInfo(con_out, &scr);
2278
cpos = scr.dwCursorPosition;
2282
SetConsoleCursorPosition(con_out, cpos);
2283
FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars);
2284
SetConsoleCursorPosition(con_out, cpos);
2293
* Output a plain backspace, without erasing the previous char.
2299
tputs(sc_backspace, 1, putchr);
2305
#if MSDOS_COMPILER==MSOFTC
2306
struct rccoord tpos;
2307
tpos = _gettextposition();
2311
#if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
2315
#if MSDOS_COMPILER==WIN32C
2316
CONSOLE_SCREEN_BUFFER_INFO scr;
2317
GetConsoleScreenBufferInfo(con_out, &scr);
2318
row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1;
2319
col = scr.dwCursorPosition.X - scr.srWindow.Left + 1;
2326
_settextposition(row, col-1);
2327
#endif /* MSDOS_COMPILER */
2330
#if MSDOS_COMPILER==WIN32C
2332
* Determine whether an input character is waiting to be read.
2344
currentKey.ascii = 0;
2345
currentKey.scan = 0;
2348
* Wait for a real key-down event, but
2349
* ignore SHIFT and CONTROL key events.
2353
PeekConsoleInput(tty, &ip, 1, &read);
2356
ReadConsoleInput(tty, &ip, 1, &read);
2357
} while (ip.EventType != KEY_EVENT ||
2358
ip.Event.KeyEvent.bKeyDown != TRUE ||
2359
ip.Event.KeyEvent.wVirtualScanCode == 0 ||
2360
ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2361
ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL ||
2362
ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU);
2364
currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar;
2365
currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode;
2366
keyCount = ip.Event.KeyEvent.wRepeatCount;
2368
if (ip.Event.KeyEvent.dwControlKeyState &
2369
(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
2371
switch (currentKey.scan)
2373
case PCK_ALT_E: /* letter 'E' */
2374
currentKey.ascii = 0;
2377
} else if (ip.Event.KeyEvent.dwControlKeyState &
2378
(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
2380
switch (currentKey.scan)
2382
case PCK_RIGHT: /* right arrow */
2383
currentKey.scan = PCK_CTL_RIGHT;
2385
case PCK_LEFT: /* left arrow */
2386
currentKey.scan = PCK_CTL_LEFT;
2388
case PCK_DELETE: /* delete */
2389
currentKey.scan = PCK_CTL_DELETE;
2397
* Read a character from the keyboard.
2405
if (pending_scancode)
2407
pending_scancode = 0;
2408
return ((char)(currentKey.scan & 0x00FF));
2411
while (win32_kbhit((HANDLE)tty) == FALSE)
2419
ascii = currentKey.ascii;
2421
* On PC's, the extended keys return a 2 byte sequence beginning
2422
* with '00', so if the ascii code is 00, the next byte will be
2423
* the lsb of the scan code.
2425
pending_scancode = (ascii == 0x00);
2426
return ((char)ascii);