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