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