GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mg/tty.c Lines: 0 158 0.0 %
Date: 2017-11-07 Branches: 0 120 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: tty.c,v 1.36 2015/11/18 18:44:50 jasper Exp $	*/
2
3
/* This file is in the public domain. */
4
5
/*
6
 * Terminfo display driver
7
 *
8
 * Terminfo is a terminal information database and routines to describe
9
 * terminals on most modern UNIX systems.  Many other systems have adopted
10
 * this as a reasonable way to allow for widely varying and ever changing
11
 * varieties of terminal types.	 This should be used where practical.
12
 */
13
/*
14
 * Known problems: If you have a terminal with no clear to end of screen and
15
 * memory of lines below the ones visible on the screen, display will be
16
 * wrong in some cases.  I doubt that any such terminal was ever made, but I
17
 * thought everyone with delete line would have clear to end of screen too...
18
 *
19
 * Code for terminals without clear to end of screen and/or clear to end of line
20
 * has not been extensively tested.
21
 *
22
 * Cost calculations are very rough.  Costs of insert/delete line may be far
23
 * from the truth.  This is accentuated by display.c not knowing about
24
 * multi-line insert/delete.
25
 *
26
 * Using scrolling region vs insert/delete line should probably be based on cost
27
 * rather than the assumption that scrolling region operations look better.
28
 */
29
30
#include <sys/ioctl.h>
31
#include <sys/queue.h>
32
#include <sys/types.h>
33
#include <sys/time.h>
34
#include <signal.h>
35
#include <stdio.h>
36
#include <term.h>
37
38
#include "def.h"
39
40
static int	 charcost(const char *);
41
42
static int	 cci;
43
static int	 insdel;	/* Do we have both insert & delete line? */
44
static const char	*scroll_fwd;	/* How to scroll forward. */
45
46
static void	 winchhandler(int);
47
48
/* ARGSUSED */
49
static void
50
winchhandler(int sig)
51
{
52
	winch_flag = 1;
53
}
54
55
/*
56
 * Initialize the terminal when the editor
57
 * gets started up.
58
 */
59
void
60
ttinit(void)
61
{
62
	int errret;
63
64
	if (setupterm(NULL, 1, &errret))
65
		panic("Terminal setup failed");
66
67
	signal(SIGWINCH, winchhandler);
68
	signal(SIGCONT, winchhandler);
69
	siginterrupt(SIGWINCH, 1);
70
71
	scroll_fwd = scroll_forward;
72
	if (scroll_fwd == NULL || *scroll_fwd == '\0') {
73
		/* this is what GNU Emacs does */
74
		scroll_fwd = parm_down_cursor;
75
		if (scroll_fwd == NULL || *scroll_fwd == '\0')
76
			scroll_fwd = "\n";
77
	}
78
79
	if (cursor_address == NULL || cursor_up == NULL)
80
		panic("This terminal is too stupid to run mg");
81
82
	/* set nrow & ncol */
83
	ttresize();
84
85
	if (!clr_eol)
86
		tceeol = ncol;
87
	else
88
		tceeol = charcost(clr_eol);
89
90
	/* Estimate cost of inserting a line */
91
	if (change_scroll_region && scroll_reverse)
92
		tcinsl = charcost(change_scroll_region) * 2 +
93
		    charcost(scroll_reverse);
94
	else if (parm_insert_line)
95
		tcinsl = charcost(parm_insert_line);
96
	else if (insert_line)
97
		tcinsl = charcost(insert_line);
98
	else
99
		/* make this cost high enough */
100
		tcinsl = nrow * ncol;
101
102
	/* Estimate cost of deleting a line */
103
	if (change_scroll_region)
104
		tcdell = charcost(change_scroll_region) * 2 +
105
		    charcost(scroll_fwd);
106
	else if (parm_delete_line)
107
		tcdell = charcost(parm_delete_line);
108
	else if (delete_line)
109
		tcdell = charcost(delete_line);
110
	else
111
		/* make this cost high enough */
112
		tcdell = nrow * ncol;
113
114
	/* Flag to indicate that we can both insert and delete lines */
115
	insdel = (insert_line || parm_insert_line) &&
116
	    (delete_line || parm_delete_line);
117
118
	if (enter_ca_mode)
119
		/* enter application mode */
120
		putpad(enter_ca_mode, 1);
121
122
	ttresize();
123
}
124
125
/*
126
 * Re-initialize the terminal when the editor is resumed.
127
 * The keypad_xmit doesn't really belong here but...
128
 */
129
void
130
ttreinit(void)
131
{
132
	/* check if file was modified while we were gone */
133
	if (fchecktime(curbp) != TRUE) {
134
		curbp->b_flag |= BFDIRTY;
135
	}
136
137
	if (enter_ca_mode)
138
		/* enter application mode */
139
		putpad(enter_ca_mode, 1);
140
141
	if (keypad_xmit)
142
		/* turn on keypad */
143
		putpad(keypad_xmit, 1);
144
145
	ttresize();
146
}
147
148
/*
149
 * Clean up the terminal, in anticipation of a return to the command
150
 * interpreter. This is a no-op on the ANSI display. On the SCALD display,
151
 * it sets the window back to half screen scrolling. Perhaps it should
152
 * query the display for the increment, and put it back to what it was.
153
 */
154
void
155
tttidy(void)
156
{
157
	ttykeymaptidy();
158
159
	/* set the term back to normal mode */
160
	if (exit_ca_mode)
161
		putpad(exit_ca_mode, 1);
162
}
163
164
/*
165
 * Move the cursor to the specified origin 0 row and column position. Try to
166
 * optimize out extra moves; redisplay may have left the cursor in the right
167
 * location last time!
168
 */
169
void
170
ttmove(int row, int col)
171
{
172
	if (ttrow != row || ttcol != col) {
173
		putpad(tgoto(cursor_address, col, row), 1);
174
		ttrow = row;
175
		ttcol = col;
176
	}
177
}
178
179
/*
180
 * Erase to end of line.
181
 */
182
void
183
tteeol(void)
184
{
185
	int	i;
186
187
	if (clr_eol)
188
		putpad(clr_eol, 1);
189
	else {
190
		i = ncol - ttcol;
191
		while (i--)
192
			ttputc(' ');
193
		ttrow = ttcol = HUGE;
194
	}
195
}
196
197
/*
198
 * Erase to end of page.
199
 */
200
void
201
tteeop(void)
202
{
203
	int	line;
204
205
	if (clr_eos)
206
		putpad(clr_eos, nrow - ttrow);
207
	else {
208
		putpad(clr_eol, 1);
209
		if (insdel)
210
			ttdell(ttrow + 1, lines, lines - ttrow - 1);
211
		else {
212
			/* do it by hand */
213
			for (line = ttrow + 1; line <= lines; ++line) {
214
				ttmove(line, 0);
215
				tteeol();
216
			}
217
		}
218
		ttrow = ttcol = HUGE;
219
	}
220
}
221
222
/*
223
 * Make a noise.
224
 */
225
void
226
ttbeep(void)
227
{
228
	putpad(bell, 1);
229
	ttflush();
230
}
231
232
/*
233
 * Insert nchunk blank line(s) onto the screen, scrolling the last line on
234
 * the screen off the bottom.  Use the scrolling region if possible for a
235
 * smoother display.  If there is no scrolling region, use a set of insert
236
 * and delete line sequences.
237
 */
238
void
239
ttinsl(int row, int bot, int nchunk)
240
{
241
	int	i, nl;
242
243
	/* One line special cases */
244
	if (row == bot) {
245
		ttmove(row, 0);
246
		tteeol();
247
		return;
248
	}
249
	/* Use scroll region and back index */
250
	if (change_scroll_region && scroll_reverse) {
251
		nl = bot - row;
252
		ttwindow(row, bot);
253
		ttmove(row, 0);
254
		while (nchunk--)
255
			putpad(scroll_reverse, nl);
256
		ttnowindow();
257
		return;
258
	/* else use insert/delete line */
259
	} else if (insdel) {
260
		ttmove(1 + bot - nchunk, 0);
261
		nl = nrow - ttrow;
262
		if (parm_delete_line)
263
			putpad(tgoto(parm_delete_line, 0, nchunk), nl);
264
		else
265
			/* For all lines in the chunk */
266
			for (i = 0; i < nchunk; i++)
267
				putpad(delete_line, nl);
268
		ttmove(row, 0);
269
270
		/* ttmove() changes ttrow */
271
		nl = nrow - ttrow;
272
273
		if (parm_insert_line)
274
			putpad(tgoto(parm_insert_line, 0, nchunk), nl);
275
		else
276
			/* For all lines in the chunk */
277
			for (i = 0; i < nchunk; i++)
278
				putpad(insert_line, nl);
279
		ttrow = HUGE;
280
		ttcol = HUGE;
281
	} else
282
		panic("ttinsl: Can't insert/delete line");
283
}
284
285
/*
286
 * Delete nchunk line(s) from "row", replacing the bottom line on the
287
 * screen with a blank line.  Unless we're using the scrolling region,
288
 * this is done with crafty sequences of insert and delete lines.  The
289
 * presence of the echo area makes a boundary condition go away.
290
 */
291
void
292
ttdell(int row, int bot, int nchunk)
293
{
294
	int	i, nl;
295
296
	/* One line special cases */
297
	if (row == bot) {
298
		ttmove(row, 0);
299
		tteeol();
300
		return;
301
	}
302
	/* scrolling region */
303
	if (change_scroll_region) {
304
		nl = bot - row;
305
		ttwindow(row, bot);
306
		ttmove(bot, 0);
307
		while (nchunk--)
308
			putpad(scroll_fwd, nl);
309
		ttnowindow();
310
	/* else use insert/delete line */
311
	} else if (insdel) {
312
		ttmove(row, 0);
313
		nl = nrow - ttrow;
314
		if (parm_delete_line)
315
			putpad(tgoto(parm_delete_line, 0, nchunk), nl);
316
		else
317
			/* For all lines in the chunk */
318
			for (i = 0; i < nchunk; i++)
319
				putpad(delete_line, nl);
320
		ttmove(1 + bot - nchunk, 0);
321
322
		/* ttmove() changes ttrow */
323
		nl = nrow - ttrow;
324
325
		if (parm_insert_line)
326
			putpad(tgoto(parm_insert_line, 0, nchunk), nl);
327
		else
328
			/* For all lines in the chunk */
329
			for (i = 0; i < nchunk; i++)
330
				putpad(insert_line, nl);
331
		ttrow = HUGE;
332
		ttcol = HUGE;
333
	} else
334
		panic("ttdell: Can't insert/delete line");
335
}
336
337
/*
338
 * This routine sets the scrolling window on the display to go from line
339
 * "top" to line "bot" (origin 0, inclusive).  The caller checks for the
340
 * pathological 1-line scroll window which doesn't work right and avoids
341
 * it.  The "ttrow" and "ttcol" variables are set to a crazy value to
342
 * ensure that the next call to "ttmove" does not turn into a no-op (the
343
 * window adjustment moves the cursor).
344
 */
345
void
346
ttwindow(int top, int bot)
347
{
348
	if (change_scroll_region && (tttop != top || ttbot != bot)) {
349
		putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
350
		ttrow = HUGE;	/* Unknown.		 */
351
		ttcol = HUGE;
352
		tttop = top;	/* Remember region.	 */
353
		ttbot = bot;
354
	}
355
}
356
357
/*
358
 * Switch to full screen scroll. This is used by "spawn.c" just before it
359
 * suspends the editor and by "display.c" when it is getting ready to
360
 * exit.  This function does a full screen scroll by telling the terminal
361
 * to set a scrolling region that is lines or nrow rows high, whichever is
362
 * larger.  This behavior seems to work right on systems where you can set
363
 * your terminal size.
364
 */
365
void
366
ttnowindow(void)
367
{
368
	if (change_scroll_region) {
369
		putpad(tgoto(change_scroll_region,
370
		    (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
371
		ttrow = HUGE;	/* Unknown.		 */
372
		ttcol = HUGE;
373
		tttop = HUGE;	/* No scroll region.	 */
374
		ttbot = HUGE;
375
	}
376
}
377
378
/*
379
 * Set the current writing color to the specified color. Watch for color
380
 * changes that are not going to do anything (the color is already right)
381
 * and don't send anything to the display.  The rainbow version does this
382
 * in putline.s on a line by line basis, so don't bother sending out the
383
 * color shift.
384
 */
385
void
386
ttcolor(int color)
387
{
388
	if (color != tthue) {
389
		if (color == CTEXT)
390
			/* normal video */
391
			putpad(exit_standout_mode, 1);
392
		else if (color == CMODE)
393
			/* reverse video */
394
			putpad(enter_standout_mode, 1);
395
		/* save the color */
396
		tthue = color;
397
	}
398
}
399
400
/*
401
 * This routine is called by the "refresh the screen" command to try
402
 * to resize the display. Look in "window.c" to see how
403
 * the caller deals with a change.
404
 *
405
 * We use `newrow' and `newcol' so vtresize() know the difference between the
406
 * new and old settings.
407
 */
408
void
409
ttresize(void)
410
{
411
	int newrow = 0, newcol = 0;
412
413
	struct	winsize winsize;
414
415
	if (ioctl(0, TIOCGWINSZ, &winsize) == 0) {
416
		newrow = winsize.ws_row;
417
		newcol = winsize.ws_col;
418
	}
419
	if ((newrow <= 0 || newcol <= 0) &&
420
	    ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
421
		newrow = 24;
422
		newcol = 80;
423
	}
424
	if (vtresize(1, newrow, newcol) != TRUE)
425
		panic("vtresize failed");
426
}
427
428
/*
429
 * fake char output for charcost()
430
 */
431
/* ARGSUSED */
432
static int
433
fakec(int c)
434
{
435
	cci++;
436
	return (0);
437
}
438
439
/* calculate the cost of doing string s */
440
static int
441
charcost(const char *s)
442
{
443
	cci = 0;
444
445
	tputs(s, nrow, fakec);
446
	return (cci);
447
}