xref: /freebsd/usr.bin/top/screen.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
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 
123be6ef06SEitan Adler /*  This file contains the routines that interface to termcap and stty/gtty.
133be6ef06SEitan Adler  *
143be6ef06SEitan Adler  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
153be6ef06SEitan Adler  *
163be6ef06SEitan Adler  *  I put in code to turn on the TOSTOP bit while top was running, but I
173be6ef06SEitan Adler  *  didn't really like the results.  If you desire it, turn on the
183be6ef06SEitan Adler  *  preprocessor variable "TOStop".   --wnl
193be6ef06SEitan Adler  */
203be6ef06SEitan Adler 
213be6ef06SEitan Adler #include <sys/ioctl.h>
22*17be5f23SEitan Adler 
23*17be5f23SEitan Adler #include <err.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 
5732497673SDaichi GOTO #define NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH 1024
5832497673SDaichi GOTO 
593be6ef06SEitan Adler void
init_termcap(bool interactive)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 */
7332497673SDaichi 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 	{
97*17be5f23SEitan Adler 	    warnx("can't open termcap file");
983be6ef06SEitan Adler 	}
993be6ef06SEitan Adler 	else
1003be6ef06SEitan Adler 	{
101*17be5f23SEitan Adler 	    warnx("no termcap entry for a `%s' terminal", term_name);
1023be6ef06SEitan Adler 	}
1033be6ef06SEitan Adler 
1043be6ef06SEitan Adler 	/* pretend it's dumb and proceed */
1050b2f6ed1SEitan Adler 	smart_terminal = false;
1063be6ef06SEitan Adler 	return;
1073be6ef06SEitan Adler     }
1083be6ef06SEitan Adler 
1093be6ef06SEitan Adler     /* "hardcopy" immediately indicates a very stupid terminal */
1103be6ef06SEitan Adler     if (tgetflag("hc"))
1113be6ef06SEitan Adler     {
1120b2f6ed1SEitan Adler 	smart_terminal = false;
1133be6ef06SEitan Adler 	return;
1143be6ef06SEitan Adler     }
1153be6ef06SEitan Adler 
1163be6ef06SEitan Adler     /* set up common terminal capabilities */
1173be6ef06SEitan Adler     if ((screen_length = tgetnum("li")) <= 0)
1183be6ef06SEitan Adler     {
1193be6ef06SEitan Adler 	screen_length = smart_terminal = 0;
1203be6ef06SEitan Adler 	return;
1213be6ef06SEitan Adler     }
1223be6ef06SEitan Adler 
1233be6ef06SEitan Adler     /* screen_width is a little different */
1243be6ef06SEitan Adler     if ((screen_width = tgetnum("co")) == -1)
1253be6ef06SEitan Adler     {
1263be6ef06SEitan Adler 	screen_width = 79;
1273be6ef06SEitan Adler     }
1283be6ef06SEitan Adler     else
1293be6ef06SEitan Adler     {
1303be6ef06SEitan Adler 	screen_width -= 1;
1313be6ef06SEitan Adler     }
1323be6ef06SEitan Adler 
1333be6ef06SEitan Adler     /* terminals that overstrike need special attention */
1343be6ef06SEitan Adler     overstrike = tgetflag("os");
1353be6ef06SEitan Adler 
1363be6ef06SEitan Adler     /* initialize the pointer into the termcap string buffer */
1373be6ef06SEitan Adler     bufptr = string_buffer;
1383be6ef06SEitan Adler 
1393be6ef06SEitan Adler     /* get "ce", clear to end */
1403be6ef06SEitan Adler     if (!overstrike)
1413be6ef06SEitan Adler     {
1423be6ef06SEitan Adler 		clear_line = tgetstr("ce", &bufptr);
1433be6ef06SEitan Adler     }
1443be6ef06SEitan Adler 
1453be6ef06SEitan Adler     /* get necessary capabilities */
1463be6ef06SEitan Adler     if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
1473be6ef06SEitan Adler 	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
1483be6ef06SEitan Adler     {
1490b2f6ed1SEitan Adler 	smart_terminal = false;
1503be6ef06SEitan Adler 	return;
1513be6ef06SEitan Adler     }
1523be6ef06SEitan Adler 
1533be6ef06SEitan Adler     /* get some more sophisticated stuff -- these are optional */
1543be6ef06SEitan Adler     clear_to_end   = tgetstr("cd", &bufptr);
1553be6ef06SEitan Adler     terminal_init  = tgetstr("ti", &bufptr);
1563be6ef06SEitan Adler     terminal_end   = tgetstr("te", &bufptr);
1573be6ef06SEitan Adler     start_standout = tgetstr("so", &bufptr);
1583be6ef06SEitan Adler     end_standout   = tgetstr("se", &bufptr);
1593be6ef06SEitan Adler 
1603be6ef06SEitan Adler     /* pad character */
1613be6ef06SEitan Adler     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
1623be6ef06SEitan Adler 
1633be6ef06SEitan Adler     /* set convenience strings */
1640059e710SEitan Adler     strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
1653be6ef06SEitan Adler     home[sizeof(home) - 1] = '\0';
1663be6ef06SEitan Adler     /* (lower_left is set in get_screensize) */
1673be6ef06SEitan Adler 
1683be6ef06SEitan Adler     /* get the actual screen size with an ioctl, if needed */
1693be6ef06SEitan Adler     /* This may change screen_width and screen_length, and it always
1703be6ef06SEitan Adler        sets lower_left. */
1713be6ef06SEitan Adler     get_screensize();
1723be6ef06SEitan Adler 
1733be6ef06SEitan Adler     /* if stdout is not a terminal, pretend we are a dumb terminal */
1740059e710SEitan Adler     if (tcgetattr(STDOUT_FILENO, &old_settings) == -1)
1753be6ef06SEitan Adler     {
1760b2f6ed1SEitan Adler 	smart_terminal = false;
1773be6ef06SEitan Adler     }
1783be6ef06SEitan Adler }
1793be6ef06SEitan Adler 
1803be6ef06SEitan Adler void
init_screen(void)181f6234b51SEitan Adler init_screen(void)
1823be6ef06SEitan Adler {
1833be6ef06SEitan Adler     /* get the old settings for safe keeping */
1840059e710SEitan Adler     if (tcgetattr(STDOUT_FILENO, &old_settings) != -1)
1853be6ef06SEitan Adler     {
1863be6ef06SEitan Adler 	/* copy the settings so we can modify them */
1873be6ef06SEitan Adler 	new_settings = old_settings;
1883be6ef06SEitan Adler 
1893be6ef06SEitan Adler 	/* turn off ICANON, character echo and tab expansion */
1903be6ef06SEitan Adler 	new_settings.c_lflag &= ~(ICANON|ECHO);
1913be6ef06SEitan Adler 	new_settings.c_oflag &= ~(TAB3);
1923be6ef06SEitan Adler 	new_settings.c_cc[VMIN] = 1;
1933be6ef06SEitan Adler 	new_settings.c_cc[VTIME] = 0;
1940059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
1953be6ef06SEitan Adler 
1963be6ef06SEitan Adler 	/* remember the erase and kill characters */
1973be6ef06SEitan Adler 	ch_erase = old_settings.c_cc[VERASE];
1983be6ef06SEitan Adler 	ch_kill  = old_settings.c_cc[VKILL];
1993be6ef06SEitan Adler 
2003be6ef06SEitan Adler 	/* remember that it really is a terminal */
2010b2f6ed1SEitan Adler 	is_a_terminal = true;
2023be6ef06SEitan Adler 
2033be6ef06SEitan Adler 	/* send the termcap initialization string */
2043be6ef06SEitan Adler 	putcap(terminal_init);
2053be6ef06SEitan Adler     }
2063be6ef06SEitan Adler 
2073be6ef06SEitan Adler     if (!is_a_terminal)
2083be6ef06SEitan Adler     {
2093be6ef06SEitan Adler 	/* not a terminal at all---consider it dumb */
2100b2f6ed1SEitan Adler 	smart_terminal = false;
2113be6ef06SEitan Adler     }
2123be6ef06SEitan Adler }
2133be6ef06SEitan Adler 
2143be6ef06SEitan Adler void
end_screen(void)215f6234b51SEitan Adler end_screen(void)
2163be6ef06SEitan Adler {
2173be6ef06SEitan Adler     /* move to the lower left, clear the line and send "te" */
2183be6ef06SEitan Adler     if (smart_terminal)
2193be6ef06SEitan Adler     {
2203be6ef06SEitan Adler 	putcap(lower_left);
2213be6ef06SEitan Adler 	putcap(clear_line);
2223be6ef06SEitan Adler 	fflush(stdout);
2233be6ef06SEitan Adler 	putcap(terminal_end);
2243be6ef06SEitan Adler     }
2253be6ef06SEitan Adler 
2263be6ef06SEitan Adler     /* if we have settings to reset, then do so */
2273be6ef06SEitan Adler     if (is_a_terminal)
2283be6ef06SEitan Adler     {
2290059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings);
2303be6ef06SEitan Adler     }
2313be6ef06SEitan Adler }
2323be6ef06SEitan Adler 
2333be6ef06SEitan Adler void
reinit_screen(void)234f6234b51SEitan Adler reinit_screen(void)
2353be6ef06SEitan Adler {
2363be6ef06SEitan Adler     /* install our settings if it is a terminal */
2373be6ef06SEitan Adler     if (is_a_terminal)
2383be6ef06SEitan Adler     {
2390059e710SEitan Adler 	tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings);
2403be6ef06SEitan Adler     }
2413be6ef06SEitan Adler 
2423be6ef06SEitan Adler     /* send init string */
2433be6ef06SEitan Adler     if (smart_terminal)
2443be6ef06SEitan Adler     {
2453be6ef06SEitan Adler 	putcap(terminal_init);
2463be6ef06SEitan Adler     }
2473be6ef06SEitan Adler }
2483be6ef06SEitan Adler 
2493be6ef06SEitan Adler void
get_screensize(void)250f6234b51SEitan Adler get_screensize(void)
2513be6ef06SEitan Adler {
2523be6ef06SEitan Adler     struct winsize ws;
2533be6ef06SEitan Adler 
2543be6ef06SEitan Adler     if (ioctl (1, TIOCGWINSZ, &ws) != -1)
2553be6ef06SEitan Adler     {
2563be6ef06SEitan Adler 	if (ws.ws_row != 0)
2573be6ef06SEitan Adler 	{
2583be6ef06SEitan Adler 	    screen_length = ws.ws_row;
2593be6ef06SEitan Adler 	}
2603be6ef06SEitan Adler 	if (ws.ws_col != 0)
2613be6ef06SEitan Adler 	{
2623be6ef06SEitan Adler 	    screen_width = ws.ws_col - 1;
2633be6ef06SEitan Adler 	}
2643be6ef06SEitan Adler     }
2653be6ef06SEitan Adler 
2663be6ef06SEitan Adler 
2673be6ef06SEitan Adler     (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
2683be6ef06SEitan Adler 	sizeof(lower_left) - 1);
2693be6ef06SEitan Adler     lower_left[sizeof(lower_left) - 1] = '\0';
2703be6ef06SEitan Adler }
2713be6ef06SEitan Adler 
2723be6ef06SEitan Adler void
top_standout(const char * msg)2734fedcd49SEitan Adler top_standout(const char *msg)
2743be6ef06SEitan Adler {
2753be6ef06SEitan Adler     if (smart_terminal)
2763be6ef06SEitan Adler     {
2773be6ef06SEitan Adler 	putcap(start_standout);
2783be6ef06SEitan Adler 	fputs(msg, stdout);
2793be6ef06SEitan Adler 	putcap(end_standout);
2803be6ef06SEitan Adler     }
2813be6ef06SEitan Adler     else
2823be6ef06SEitan Adler     {
2833be6ef06SEitan Adler 	fputs(msg, stdout);
2843be6ef06SEitan Adler     }
2853be6ef06SEitan Adler }
2863be6ef06SEitan Adler 
2873be6ef06SEitan Adler void
top_clear(void)288f6234b51SEitan Adler top_clear(void)
2893be6ef06SEitan Adler {
2903be6ef06SEitan Adler     if (smart_terminal)
2913be6ef06SEitan Adler     {
2923be6ef06SEitan Adler 	putcap(clear_screen);
2933be6ef06SEitan Adler     }
2943be6ef06SEitan Adler }
2953be6ef06SEitan Adler 
2963be6ef06SEitan Adler int
clear_eol(int len)2973be6ef06SEitan Adler clear_eol(int len)
2983be6ef06SEitan Adler {
2993be6ef06SEitan Adler     if (smart_terminal && !overstrike && len > 0)
3003be6ef06SEitan Adler     {
3013be6ef06SEitan Adler 	if (clear_line)
3023be6ef06SEitan Adler 	{
3033be6ef06SEitan Adler 	    putcap(clear_line);
3043be6ef06SEitan Adler 	    return(0);
3053be6ef06SEitan Adler 	}
3063be6ef06SEitan Adler 	else
3073be6ef06SEitan Adler 	{
3083be6ef06SEitan Adler 	    while (len-- > 0)
3093be6ef06SEitan Adler 	    {
3103be6ef06SEitan Adler 		putchar(' ');
3113be6ef06SEitan Adler 	    }
3123be6ef06SEitan Adler 	    return(1);
3133be6ef06SEitan Adler 	}
3143be6ef06SEitan Adler     }
3153be6ef06SEitan Adler     return(-1);
3163be6ef06SEitan Adler }
317