xref: /freebsd/usr.bin/top/screen.c (revision 0957b409)
1 /*
2  *  Top users/processes display for Unix
3  *  Version 3
4  *
5  *  This program may be freely redistributed,
6  *  but this entire comment MUST remain intact.
7  *
8  *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
9  *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
10  *
11  * $FreeBSD$
12  */
13 
14 /*  This file contains the routines that interface to termcap and stty/gtty.
15  *
16  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
17  *
18  *  I put in code to turn on the TOSTOP bit while top was running, but I
19  *  didn't really like the results.  If you desire it, turn on the
20  *  preprocessor variable "TOStop".   --wnl
21  */
22 
23 #include <sys/ioctl.h>
24 
25 #include <err.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <termios.h>
29 #include <curses.h>
30 #include <termcap.h>
31 #include <unistd.h>
32 
33 #include "screen.h"
34 #include "top.h"
35 
36 int  overstrike;
37 int  screen_length;
38 int  screen_width;
39 char ch_erase;
40 char ch_kill;
41 char smart_terminal;
42 static char termcap_buf[1024];
43 static char string_buffer[1024];
44 static char home[15];
45 static char lower_left[15];
46 char *clear_line;
47 static char *clear_screen;
48 char *clear_to_end;
49 char *cursor_motion;
50 static char *start_standout;
51 static char *end_standout;
52 static char *terminal_init;
53 static char *terminal_end;
54 
55 static struct termios old_settings;
56 static struct termios new_settings;
57 static char is_a_terminal = false;
58 
59 #define NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH 1024
60 
61 void
62 init_termcap(bool interactive)
63 {
64     char *bufptr;
65     char *PCptr;
66     char *term_name;
67     int status;
68 
69     screen_width = 0;
70     screen_length = 0;
71 
72     if (!interactive)
73     {
74 	/* pretend we have a dumb terminal */
75 	screen_width = NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH;
76 	smart_terminal = false;
77 	return;
78     }
79 
80     /* assume we have a smart terminal until proven otherwise */
81     smart_terminal = true;
82 
83     /* get the terminal name */
84     term_name = getenv("TERM");
85 
86     /* if there is no TERM, assume it's a dumb terminal */
87     /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
88     if (term_name == NULL)
89     {
90 	smart_terminal = false;
91 	return;
92     }
93 
94     /* now get the termcap entry */
95     if ((status = tgetent(termcap_buf, term_name)) != 1)
96     {
97 	if (status == -1)
98 	{
99 	    warnx("can't open termcap file");
100 	}
101 	else
102 	{
103 	    warnx("no termcap entry for a `%s' terminal", term_name);
104 	}
105 
106 	/* pretend it's dumb and proceed */
107 	smart_terminal = false;
108 	return;
109     }
110 
111     /* "hardcopy" immediately indicates a very stupid terminal */
112     if (tgetflag("hc"))
113     {
114 	smart_terminal = false;
115 	return;
116     }
117 
118     /* set up common terminal capabilities */
119     if ((screen_length = tgetnum("li")) <= 0)
120     {
121 	screen_length = smart_terminal = 0;
122 	return;
123     }
124 
125     /* screen_width is a little different */
126     if ((screen_width = tgetnum("co")) == -1)
127     {
128 	screen_width = 79;
129     }
130     else
131     {
132 	screen_width -= 1;
133     }
134 
135     /* terminals that overstrike need special attention */
136     overstrike = tgetflag("os");
137 
138     /* initialize the pointer into the termcap string buffer */
139     bufptr = string_buffer;
140 
141     /* get "ce", clear to end */
142     if (!overstrike)
143     {
144 		clear_line = tgetstr("ce", &bufptr);
145     }
146 
147     /* get necessary capabilities */
148     if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
149 	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
150     {
151 	smart_terminal = false;
152 	return;
153     }
154 
155     /* get some more sophisticated stuff -- these are optional */
156     clear_to_end   = tgetstr("cd", &bufptr);
157     terminal_init  = tgetstr("ti", &bufptr);
158     terminal_end   = tgetstr("te", &bufptr);
159     start_standout = tgetstr("so", &bufptr);
160     end_standout   = tgetstr("se", &bufptr);
161 
162     /* pad character */
163     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
164 
165     /* set convenience strings */
166     strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
167     home[sizeof(home) - 1] = '\0';
168     /* (lower_left is set in get_screensize) */
169 
170     /* get the actual screen size with an ioctl, if needed */
171     /* This may change screen_width and screen_length, and it always
172        sets lower_left. */
173     get_screensize();
174 
175     /* if stdout is not a terminal, pretend we are a dumb terminal */
176     if (tcgetattr(STDOUT_FILENO, &old_settings) == -1)
177     {
178 	smart_terminal = false;
179     }
180 }
181 
182 void
183 init_screen(void)
184 {
185     /* get the old settings for safe keeping */
186     if (tcgetattr(STDOUT_FILENO, &old_settings) != -1)
187     {
188 	/* copy the settings so we can modify them */
189 	new_settings = old_settings;
190 
191 	/* turn off ICANON, character echo and tab expansion */
192 	new_settings.c_lflag &= ~(ICANON|ECHO);
193 	new_settings.c_oflag &= ~(TAB3);
194 	new_settings.c_cc[VMIN] = 1;
195 	new_settings.c_cc[VTIME] = 0;
196 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
197 
198 	/* remember the erase and kill characters */
199 	ch_erase = old_settings.c_cc[VERASE];
200 	ch_kill  = old_settings.c_cc[VKILL];
201 
202 	/* remember that it really is a terminal */
203 	is_a_terminal = true;
204 
205 	/* send the termcap initialization string */
206 	putcap(terminal_init);
207     }
208 
209     if (!is_a_terminal)
210     {
211 	/* not a terminal at all---consider it dumb */
212 	smart_terminal = false;
213     }
214 }
215 
216 void
217 end_screen(void)
218 {
219     /* move to the lower left, clear the line and send "te" */
220     if (smart_terminal)
221     {
222 	putcap(lower_left);
223 	putcap(clear_line);
224 	fflush(stdout);
225 	putcap(terminal_end);
226     }
227 
228     /* if we have settings to reset, then do so */
229     if (is_a_terminal)
230     {
231 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings);
232     }
233 }
234 
235 void
236 reinit_screen(void)
237 {
238     /* install our settings if it is a terminal */
239     if (is_a_terminal)
240     {
241 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
242     }
243 
244     /* send init string */
245     if (smart_terminal)
246     {
247 	putcap(terminal_init);
248     }
249 }
250 
251 void
252 get_screensize(void)
253 {
254     struct winsize ws;
255 
256     if (ioctl (1, TIOCGWINSZ, &ws) != -1)
257     {
258 	if (ws.ws_row != 0)
259 	{
260 	    screen_length = ws.ws_row;
261 	}
262 	if (ws.ws_col != 0)
263 	{
264 	    screen_width = ws.ws_col - 1;
265 	}
266     }
267 
268 
269     (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
270 	sizeof(lower_left) - 1);
271     lower_left[sizeof(lower_left) - 1] = '\0';
272 }
273 
274 void
275 top_standout(const char *msg)
276 {
277     if (smart_terminal)
278     {
279 	putcap(start_standout);
280 	fputs(msg, stdout);
281 	putcap(end_standout);
282     }
283     else
284     {
285 	fputs(msg, stdout);
286     }
287 }
288 
289 void
290 top_clear(void)
291 {
292     if (smart_terminal)
293     {
294 	putcap(clear_screen);
295     }
296 }
297 
298 int
299 clear_eol(int len)
300 {
301     if (smart_terminal && !overstrike && len > 0)
302     {
303 	if (clear_line)
304 	{
305 	    putcap(clear_line);
306 	    return(0);
307 	}
308 	else
309 	{
310 	    while (len-- > 0)
311 	    {
312 		putchar(' ');
313 	    }
314 	    return(1);
315 	}
316     }
317     return(-1);
318 }
319