xref: /openbsd-src/usr.bin/top/screen.c (revision 8500990981f885cbe5e6a4958549cacc238b5ae6)
1 /* $OpenBSD: screen.c,v 1.13 2003/06/16 17:24:44 millert Exp $	 */
2 
3 /*
4  *  Top users/processes display for Unix
5  *  Version 3
6  *
7  * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8  * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * This file contains the routines that interface to termcap and stty/gtty.
33  *
34  * Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
35  *
36  * I put in code to turn on the TOSTOP bit while top was running, but I didn't
37  * really like the results.  If you desire it, turn on the preprocessor
38  * variable "TOStop".   --wnl
39  */
40 
41 #include <sys/types.h>
42 #include <sys/ioctl.h>
43 #include <err.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <term.h>
48 #include <termios.h>
49 #include <unistd.h>
50 
51 #include "top.h"
52 #include "screen.h"
53 #include "boolean.h"
54 
55 int	overstrike, screen_length, screen_width;
56 char	ch_erase, ch_kill, smart_terminal, PC;
57 char	string_buffer[1024], home[15], lower_left[15];
58 char	*clear_line, *clear_scr, *clear_to_end;
59 char	*cursor_motion, *start_standout, *end_standout;
60 char	*terminal_init, *terminal_end;
61 short	ospeed;
62 
63 static struct termios old_settings, new_settings;
64 
65 static char	is_a_terminal = No;
66 
67 void
68 init_termcap(int interactive)
69 {
70 	char *bufptr, *PCptr, *term_name;
71 	int status;
72 
73 	/* set defaults in case we aren't smart */
74 	screen_width = MAX_COLS;
75 	screen_length = 0;
76 
77 	if (!interactive) {
78 		/* pretend we have a dumb terminal */
79 		smart_terminal = No;
80 		return;
81 	}
82 	/* assume we have a smart terminal until proven otherwise */
83 	smart_terminal = Yes;
84 
85 	/* get the terminal name */
86 	term_name = getenv("TERM");
87 
88 	/* if there is no TERM, assume it's a dumb terminal */
89 	/* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
90 	if (term_name == NULL) {
91 		smart_terminal = No;
92 		return;
93 	}
94 
95 	/* now get the termcap entry */
96 	if ((status = tgetent(NULL, term_name)) != 1) {
97 		if (status == -1)
98 			warnx("can't open termcap file");
99 		else
100 			warnx("no termcap entry for a `%s' terminal", term_name);
101 
102 		/* pretend it's dumb and proceed */
103 		smart_terminal = No;
104 		return;
105 	}
106 
107 	/* "hardcopy" immediately indicates a very stupid terminal */
108 	if (tgetflag("hc")) {
109 		smart_terminal = No;
110 		return;
111 	}
112 	/* set up common terminal capabilities */
113 	if ((screen_length = tgetnum("li")) <= Header_lines) {
114 		screen_length = smart_terminal = 0;
115 		return;
116 	}
117 
118 	/* screen_width is a little different */
119 	if ((screen_width = tgetnum("co")) == -1)
120 		screen_width = 79;
121 	else
122 		screen_width -= 1;
123 
124 	/* terminals that overstrike need special attention */
125 	overstrike = tgetflag("os");
126 
127 	/* initialize the pointer into the termcap string buffer */
128 	bufptr = string_buffer;
129 
130 	/* get "ce", clear to end */
131 	if (!overstrike) {
132 		clear_line = tgetstr("ce", &bufptr);
133 	}
134 	/* get necessary capabilities */
135 	if ((clear_scr = tgetstr("cl", &bufptr)) == NULL ||
136 	    (cursor_motion = tgetstr("cm", &bufptr)) == NULL) {
137 		smart_terminal = No;
138 		return;
139 	}
140 	/* get some more sophisticated stuff -- these are optional */
141 	clear_to_end = tgetstr("cd", &bufptr);
142 	terminal_init = tgetstr("ti", &bufptr);
143 	terminal_end = tgetstr("te", &bufptr);
144 	start_standout = tgetstr("so", &bufptr);
145 	end_standout = tgetstr("se", &bufptr);
146 
147 	/* pad character */
148 	PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
149 
150 	/* set convenience strings */
151 	(void) strlcpy(home, tgoto(cursor_motion, 0, 0), sizeof(home));
152 	/* (lower_left is set in get_screensize) */
153 
154 	/* get the actual screen size with an ioctl, if needed */
155 	/*
156 	 * This may change screen_width and screen_length, and it always sets
157 	 * lower_left.
158 	 */
159 	get_screensize();
160 
161 	/* if stdout is not a terminal, pretend we are a dumb terminal */
162 	if (tcgetattr(STDOUT_FILENO, &old_settings) == -1)
163 		smart_terminal = No;
164 }
165 
166 void
167 init_screen(void)
168 {
169 	/* get the old settings for safe keeping */
170 	if (tcgetattr(STDOUT_FILENO, &old_settings) != -1) {
171 		/* copy the settings so we can modify them */
172 		new_settings = old_settings;
173 
174 		/* turn off ICANON, character echo and tab expansion */
175 		new_settings.c_lflag &= ~(ICANON | ECHO);
176 		new_settings.c_oflag &= ~(OXTABS);
177 		new_settings.c_cc[VMIN] = 1;
178 		new_settings.c_cc[VTIME] = 0;
179 		(void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
180 
181 		/* remember the erase and kill characters */
182 		ch_erase = old_settings.c_cc[VERASE];
183 		ch_kill = old_settings.c_cc[VKILL];
184 
185 		/* remember that it really is a terminal */
186 		is_a_terminal = Yes;
187 
188 		/* send the termcap initialization string */
189 		putcap(terminal_init);
190 	}
191 	if (!is_a_terminal) {
192 		/* not a terminal at all---consider it dumb */
193 		smart_terminal = No;
194 	}
195 }
196 
197 void
198 end_screen(void)
199 {
200 	/* move to the lower left, clear the line and send "te" */
201 	if (smart_terminal) {
202 		putcap(lower_left);
203 		putcap(clear_line);
204 		fflush(stdout);
205 		putcap(terminal_end);
206 	}
207 
208 	/* if we have settings to reset, then do so */
209 	if (is_a_terminal)
210 		(void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings);
211 }
212 
213 void
214 reinit_screen(void)
215 {
216 	/* install our settings if it is a terminal */
217 	if (is_a_terminal)
218 		(void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
219 
220 	/* send init string */
221 	if (smart_terminal)
222 		putcap(terminal_init);
223 }
224 
225 void
226 get_screensize(void)
227 {
228 	struct winsize ws;
229 
230 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
231 		if (ws.ws_row != 0)
232 			screen_length = ws.ws_row;
233 		if (ws.ws_col != 0)
234 			screen_width = ws.ws_col - 1;
235 	}
236 	(void) strlcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
237 	    sizeof(lower_left));
238 }
239 
240 void
241 standout(char *msg)
242 {
243 	if (smart_terminal) {
244 		putcap(start_standout);
245 		if (fputs(msg, stdout) == EOF)
246 			exit(1);
247 		putcap(end_standout);
248 	} else {
249 		if (fputs(msg, stdout) == EOF)
250 			exit(1);
251 	}
252 }
253 
254 void
255 clear()
256 {
257 	if (smart_terminal)
258 		putcap(clear_scr);
259 }
260 
261 int
262 clear_eol(int len)
263 {
264 	if (smart_terminal && !overstrike && len > 0) {
265 		if (clear_line) {
266 			putcap(clear_line);
267 			return (0);
268 		} else {
269 			while (len-- > 0) {
270 				if (putchar(' ') == EOF)
271 					exit(1);
272 			}
273 			return (1);
274 		}
275 	}
276 	return (-1);
277 }
278 
279 void
280 go_home(void)
281 {
282 	if (smart_terminal)
283 		putcap(home);
284 }
285 
286 /* This has to be defined as a subroutine for tputs (instead of a macro) */
287 int
288 putstdout(int ch)
289 {
290 	int ret;
291 
292 	ret = putchar(ch);
293 	if (ret == EOF)
294 		exit(1);
295 	return (ret);
296 }
297