xref: /freebsd/usr.bin/top/screen.c (revision 324976739f3c03838bf53eb4edbbac736b9d87d8)
13be6ef06SEitan Adler /*
23be6ef06SEitan Adler  *  Top users/processes display for Unix
33be6ef06SEitan Adler  *  Version 3
43be6ef06SEitan Adler  *
53be6ef06SEitan Adler  *  This program may be freely redistributed,
6c51e28f4SEitan Adler  *  but this entire comment MUST remain intact.
73be6ef06SEitan Adler  *
83be6ef06SEitan Adler  *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
93be6ef06SEitan Adler  *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
103be6ef06SEitan Adler  *
113be6ef06SEitan Adler  * $FreeBSD$
123be6ef06SEitan Adler  */
133be6ef06SEitan Adler 
143be6ef06SEitan Adler /*  This file contains the routines that interface to termcap and stty/gtty.
153be6ef06SEitan Adler  *
163be6ef06SEitan Adler  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
173be6ef06SEitan Adler  *
183be6ef06SEitan Adler  *  I put in code to turn on the TOSTOP bit while top was running, but I
193be6ef06SEitan Adler  *  didn't really like the results.  If you desire it, turn on the
203be6ef06SEitan Adler  *  preprocessor variable "TOStop".   --wnl
213be6ef06SEitan Adler  */
223be6ef06SEitan Adler 
233be6ef06SEitan Adler #include <sys/ioctl.h>
24666cf873SEitan Adler #include <stdlib.h>
25caee4883SEitan Adler #include <string.h>
263be6ef06SEitan Adler #include <termios.h>
273be6ef06SEitan Adler #include <curses.h>
283be6ef06SEitan Adler #include <termcap.h>
290059e710SEitan Adler #include <unistd.h>
300059e710SEitan Adler 
313be6ef06SEitan Adler #include "screen.h"
320059e710SEitan Adler #include "top.h"
333be6ef06SEitan Adler 
343be6ef06SEitan Adler int  overstrike;
353be6ef06SEitan Adler int  screen_length;
363be6ef06SEitan Adler int  screen_width;
373be6ef06SEitan Adler char ch_erase;
383be6ef06SEitan Adler char ch_kill;
393be6ef06SEitan Adler char smart_terminal;
401b7645c6SEitan Adler static char termcap_buf[1024];
411b7645c6SEitan Adler static char string_buffer[1024];
421b7645c6SEitan Adler static char home[15];
431b7645c6SEitan Adler static char lower_left[15];
443be6ef06SEitan Adler char *clear_line;
451b7645c6SEitan Adler static char *clear_screen;
463be6ef06SEitan Adler char *clear_to_end;
473be6ef06SEitan Adler char *cursor_motion;
481b7645c6SEitan Adler static char *start_standout;
491b7645c6SEitan Adler static char *end_standout;
501b7645c6SEitan Adler static char *terminal_init;
511b7645c6SEitan Adler static char *terminal_end;
523be6ef06SEitan Adler 
533be6ef06SEitan Adler static struct termios old_settings;
543be6ef06SEitan Adler static struct termios new_settings;
550b2f6ed1SEitan Adler static char is_a_terminal = false;
563be6ef06SEitan Adler 
57*32497673SDaichi GOTO #define NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH 1024
58*32497673SDaichi GOTO 
593be6ef06SEitan Adler void
605c66dcc0SEitan Adler init_termcap(bool interactive)
613be6ef06SEitan Adler {
623be6ef06SEitan Adler     char *bufptr;
633be6ef06SEitan Adler     char *PCptr;
643be6ef06SEitan Adler     char *term_name;
653be6ef06SEitan Adler     int status;
663be6ef06SEitan Adler 
670a59db87SDaichi GOTO     screen_width = 0;
683be6ef06SEitan Adler     screen_length = 0;
693be6ef06SEitan Adler 
703be6ef06SEitan Adler     if (!interactive)
713be6ef06SEitan Adler     {
723be6ef06SEitan Adler 	/* pretend we have a dumb terminal */
73*32497673SDaichi GOTO 	screen_width = NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH;
740b2f6ed1SEitan Adler 	smart_terminal = false;
753be6ef06SEitan Adler 	return;
763be6ef06SEitan Adler     }
773be6ef06SEitan Adler 
783be6ef06SEitan Adler     /* assume we have a smart terminal until proven otherwise */
790b2f6ed1SEitan Adler     smart_terminal = true;
803be6ef06SEitan Adler 
813be6ef06SEitan Adler     /* get the terminal name */
823be6ef06SEitan Adler     term_name = getenv("TERM");
833be6ef06SEitan Adler 
843be6ef06SEitan Adler     /* if there is no TERM, assume it's a dumb terminal */
853be6ef06SEitan Adler     /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
863be6ef06SEitan Adler     if (term_name == NULL)
873be6ef06SEitan Adler     {
880b2f6ed1SEitan Adler 	smart_terminal = false;
893be6ef06SEitan Adler 	return;
903be6ef06SEitan Adler     }
913be6ef06SEitan Adler 
923be6ef06SEitan Adler     /* now get the termcap entry */
933be6ef06SEitan Adler     if ((status = tgetent(termcap_buf, term_name)) != 1)
943be6ef06SEitan Adler     {
953be6ef06SEitan Adler 	if (status == -1)
963be6ef06SEitan Adler 	{
973be6ef06SEitan Adler 	    fprintf(stderr, "%s: can't open termcap file\n", myname);
983be6ef06SEitan Adler 	}
993be6ef06SEitan Adler 	else
1003be6ef06SEitan Adler 	{
1013be6ef06SEitan Adler 	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
1023be6ef06SEitan Adler 		    myname, term_name);
1033be6ef06SEitan Adler 	}
1043be6ef06SEitan Adler 
1053be6ef06SEitan Adler 	/* pretend it's dumb and proceed */
1060b2f6ed1SEitan Adler 	smart_terminal = false;
1073be6ef06SEitan Adler 	return;
1083be6ef06SEitan Adler     }
1093be6ef06SEitan Adler 
1103be6ef06SEitan Adler     /* "hardcopy" immediately indicates a very stupid terminal */
1113be6ef06SEitan Adler     if (tgetflag("hc"))
1123be6ef06SEitan Adler     {
1130b2f6ed1SEitan Adler 	smart_terminal = false;
1143be6ef06SEitan Adler 	return;
1153be6ef06SEitan Adler     }
1163be6ef06SEitan Adler 
1173be6ef06SEitan Adler     /* set up common terminal capabilities */
1183be6ef06SEitan Adler     if ((screen_length = tgetnum("li")) <= 0)
1193be6ef06SEitan Adler     {
1203be6ef06SEitan Adler 	screen_length = smart_terminal = 0;
1213be6ef06SEitan Adler 	return;
1223be6ef06SEitan Adler     }
1233be6ef06SEitan Adler 
1243be6ef06SEitan Adler     /* screen_width is a little different */
1253be6ef06SEitan Adler     if ((screen_width = tgetnum("co")) == -1)
1263be6ef06SEitan Adler     {
1273be6ef06SEitan Adler 	screen_width = 79;
1283be6ef06SEitan Adler     }
1293be6ef06SEitan Adler     else
1303be6ef06SEitan Adler     {
1313be6ef06SEitan Adler 	screen_width -= 1;
1323be6ef06SEitan Adler     }
1333be6ef06SEitan Adler 
1343be6ef06SEitan Adler     /* terminals that overstrike need special attention */
1353be6ef06SEitan Adler     overstrike = tgetflag("os");
1363be6ef06SEitan Adler 
1373be6ef06SEitan Adler     /* initialize the pointer into the termcap string buffer */
1383be6ef06SEitan Adler     bufptr = string_buffer;
1393be6ef06SEitan Adler 
1403be6ef06SEitan Adler     /* get "ce", clear to end */
1413be6ef06SEitan Adler     if (!overstrike)
1423be6ef06SEitan Adler     {
1433be6ef06SEitan Adler 		clear_line = tgetstr("ce", &bufptr);
1443be6ef06SEitan Adler     }
1453be6ef06SEitan Adler 
1463be6ef06SEitan Adler     /* get necessary capabilities */
1473be6ef06SEitan Adler     if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
1483be6ef06SEitan Adler 	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
1493be6ef06SEitan Adler     {
1500b2f6ed1SEitan Adler 	smart_terminal = false;
1513be6ef06SEitan Adler 	return;
1523be6ef06SEitan Adler     }
1533be6ef06SEitan Adler 
1543be6ef06SEitan Adler     /* get some more sophisticated stuff -- these are optional */
1553be6ef06SEitan Adler     clear_to_end   = tgetstr("cd", &bufptr);
1563be6ef06SEitan Adler     terminal_init  = tgetstr("ti", &bufptr);
1573be6ef06SEitan Adler     terminal_end   = tgetstr("te", &bufptr);
1583be6ef06SEitan Adler     start_standout = tgetstr("so", &bufptr);
1593be6ef06SEitan Adler     end_standout   = tgetstr("se", &bufptr);
1603be6ef06SEitan Adler 
1613be6ef06SEitan Adler     /* pad character */
1623be6ef06SEitan Adler     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
1633be6ef06SEitan Adler 
1643be6ef06SEitan Adler     /* set convenience strings */
1650059e710SEitan Adler     strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
1663be6ef06SEitan Adler     home[sizeof(home) - 1] = '\0';
1673be6ef06SEitan Adler     /* (lower_left is set in get_screensize) */
1683be6ef06SEitan Adler 
1693be6ef06SEitan Adler     /* get the actual screen size with an ioctl, if needed */
1703be6ef06SEitan Adler     /* This may change screen_width and screen_length, and it always
1713be6ef06SEitan Adler        sets lower_left. */
1723be6ef06SEitan Adler     get_screensize();
1733be6ef06SEitan Adler 
1743be6ef06SEitan Adler     /* if stdout is not a terminal, pretend we are a dumb terminal */
1750059e710SEitan Adler     if (tcgetattr(STDOUT_FILENO, &old_settings) == -1)
1763be6ef06SEitan Adler     {
1770b2f6ed1SEitan Adler 	smart_terminal = false;
1783be6ef06SEitan Adler     }
1793be6ef06SEitan Adler }
1803be6ef06SEitan Adler 
1813be6ef06SEitan Adler void
182f6234b51SEitan Adler init_screen(void)
1833be6ef06SEitan Adler {
1843be6ef06SEitan Adler     /* get the old settings for safe keeping */
1850059e710SEitan Adler     if (tcgetattr(STDOUT_FILENO, &old_settings) != -1)
1863be6ef06SEitan Adler     {
1873be6ef06SEitan Adler 	/* copy the settings so we can modify them */
1883be6ef06SEitan Adler 	new_settings = old_settings;
1893be6ef06SEitan Adler 
1903be6ef06SEitan Adler 	/* turn off ICANON, character echo and tab expansion */
1913be6ef06SEitan Adler 	new_settings.c_lflag &= ~(ICANON|ECHO);
1923be6ef06SEitan Adler 	new_settings.c_oflag &= ~(TAB3);
1933be6ef06SEitan Adler 	new_settings.c_cc[VMIN] = 1;
1943be6ef06SEitan Adler 	new_settings.c_cc[VTIME] = 0;
1950059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
1963be6ef06SEitan Adler 
1973be6ef06SEitan Adler 	/* remember the erase and kill characters */
1983be6ef06SEitan Adler 	ch_erase = old_settings.c_cc[VERASE];
1993be6ef06SEitan Adler 	ch_kill  = old_settings.c_cc[VKILL];
2003be6ef06SEitan Adler 
2013be6ef06SEitan Adler 	/* remember that it really is a terminal */
2020b2f6ed1SEitan Adler 	is_a_terminal = true;
2033be6ef06SEitan Adler 
2043be6ef06SEitan Adler 	/* send the termcap initialization string */
2053be6ef06SEitan Adler 	putcap(terminal_init);
2063be6ef06SEitan Adler     }
2073be6ef06SEitan Adler 
2083be6ef06SEitan Adler     if (!is_a_terminal)
2093be6ef06SEitan Adler     {
2103be6ef06SEitan Adler 	/* not a terminal at all---consider it dumb */
2110b2f6ed1SEitan Adler 	smart_terminal = false;
2123be6ef06SEitan Adler     }
2133be6ef06SEitan Adler }
2143be6ef06SEitan Adler 
2153be6ef06SEitan Adler void
216f6234b51SEitan Adler end_screen(void)
2173be6ef06SEitan Adler {
2183be6ef06SEitan Adler     /* move to the lower left, clear the line and send "te" */
2193be6ef06SEitan Adler     if (smart_terminal)
2203be6ef06SEitan Adler     {
2213be6ef06SEitan Adler 	putcap(lower_left);
2223be6ef06SEitan Adler 	putcap(clear_line);
2233be6ef06SEitan Adler 	fflush(stdout);
2243be6ef06SEitan Adler 	putcap(terminal_end);
2253be6ef06SEitan Adler     }
2263be6ef06SEitan Adler 
2273be6ef06SEitan Adler     /* if we have settings to reset, then do so */
2283be6ef06SEitan Adler     if (is_a_terminal)
2293be6ef06SEitan Adler     {
2300059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings);
2313be6ef06SEitan Adler     }
2323be6ef06SEitan Adler }
2333be6ef06SEitan Adler 
2343be6ef06SEitan Adler void
235f6234b51SEitan Adler reinit_screen(void)
2363be6ef06SEitan Adler {
2373be6ef06SEitan Adler     /* install our settings if it is a terminal */
2383be6ef06SEitan Adler     if (is_a_terminal)
2393be6ef06SEitan Adler     {
2400059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
2413be6ef06SEitan Adler     }
2423be6ef06SEitan Adler 
2433be6ef06SEitan Adler     /* send init string */
2443be6ef06SEitan Adler     if (smart_terminal)
2453be6ef06SEitan Adler     {
2463be6ef06SEitan Adler 	putcap(terminal_init);
2473be6ef06SEitan Adler     }
2483be6ef06SEitan Adler }
2493be6ef06SEitan Adler 
2503be6ef06SEitan Adler void
251f6234b51SEitan Adler get_screensize(void)
2523be6ef06SEitan Adler {
2533be6ef06SEitan Adler     struct winsize ws;
2543be6ef06SEitan Adler 
2553be6ef06SEitan Adler     if (ioctl (1, TIOCGWINSZ, &ws) != -1)
2563be6ef06SEitan Adler     {
2573be6ef06SEitan Adler 	if (ws.ws_row != 0)
2583be6ef06SEitan Adler 	{
2593be6ef06SEitan Adler 	    screen_length = ws.ws_row;
2603be6ef06SEitan Adler 	}
2613be6ef06SEitan Adler 	if (ws.ws_col != 0)
2623be6ef06SEitan Adler 	{
2633be6ef06SEitan Adler 	    screen_width = ws.ws_col - 1;
2643be6ef06SEitan Adler 	}
2653be6ef06SEitan Adler     }
2663be6ef06SEitan Adler 
2673be6ef06SEitan Adler 
2683be6ef06SEitan Adler     (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
2693be6ef06SEitan Adler 	sizeof(lower_left) - 1);
2703be6ef06SEitan Adler     lower_left[sizeof(lower_left) - 1] = '\0';
2713be6ef06SEitan Adler }
2723be6ef06SEitan Adler 
2733be6ef06SEitan Adler void
2744fedcd49SEitan Adler top_standout(const char *msg)
2753be6ef06SEitan Adler {
2763be6ef06SEitan Adler     if (smart_terminal)
2773be6ef06SEitan Adler     {
2783be6ef06SEitan Adler 	putcap(start_standout);
2793be6ef06SEitan Adler 	fputs(msg, stdout);
2803be6ef06SEitan Adler 	putcap(end_standout);
2813be6ef06SEitan Adler     }
2823be6ef06SEitan Adler     else
2833be6ef06SEitan Adler     {
2843be6ef06SEitan Adler 	fputs(msg, stdout);
2853be6ef06SEitan Adler     }
2863be6ef06SEitan Adler }
2873be6ef06SEitan Adler 
2883be6ef06SEitan Adler void
289f6234b51SEitan Adler top_clear(void)
2903be6ef06SEitan Adler {
2913be6ef06SEitan Adler     if (smart_terminal)
2923be6ef06SEitan Adler     {
2933be6ef06SEitan Adler 	putcap(clear_screen);
2943be6ef06SEitan Adler     }
2953be6ef06SEitan Adler }
2963be6ef06SEitan Adler 
2973be6ef06SEitan Adler int
2983be6ef06SEitan Adler clear_eol(int len)
2993be6ef06SEitan Adler {
3003be6ef06SEitan Adler     if (smart_terminal && !overstrike && len > 0)
3013be6ef06SEitan Adler     {
3023be6ef06SEitan Adler 	if (clear_line)
3033be6ef06SEitan Adler 	{
3043be6ef06SEitan Adler 	    putcap(clear_line);
3053be6ef06SEitan Adler 	    return(0);
3063be6ef06SEitan Adler 	}
3073be6ef06SEitan Adler 	else
3083be6ef06SEitan Adler 	{
3093be6ef06SEitan Adler 	    while (len-- > 0)
3103be6ef06SEitan Adler 	    {
3113be6ef06SEitan Adler 		putchar(' ');
3123be6ef06SEitan Adler 	    }
3133be6ef06SEitan Adler 	    return(1);
3143be6ef06SEitan Adler 	}
3153be6ef06SEitan Adler     }
3163be6ef06SEitan Adler     return(-1);
3173be6ef06SEitan Adler }
318