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