Updated the boilerplate on each file
[citadel.git] / textclient / screen.c
1 // Screen output handling
2 //
3 // Copyright (c) 1987-2021 by the citadel.org team
4 //
5 // This program is open source software.  Use, duplication, and/or
6 // disclosure are subject to the GNU General Purpose License version 3.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12
13 #include "textclient.h"
14
15 int enable_status_line = 0;
16 char status_line[1024] = "     ";
17
18 /* the default paginator prompt will be replaced by the server's prompt when we learn it */
19 char *moreprompt = " -- more -- ";
20
21 int screenheight = 24;
22 int screenwidth = 80;
23 int lines_printed = 0;
24 int cols_printed = 0;
25
26 extern int rc_ansi_color;
27 extern int rc_prompt_control;
28 void do_keepalive(void);
29
30 /*
31  * Attempt to discover the screen dimensions. 
32  * WARNING: This is sometimes called from a signal handler.
33  */
34 void check_screen_dims(void) {
35 #ifdef TIOCGWINSZ
36         struct {
37                 unsigned short height;  /* rows */
38                 unsigned short width;   /* columns */
39                 unsigned short xpixels;
40                 unsigned short ypixels; /* pixels */
41         } xwinsz;
42
43         if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
44                 if (xwinsz.height)
45                         screenheight = (int) xwinsz.height;
46                 if (xwinsz.width)
47                         screenwidth = (int) xwinsz.width;
48         }
49 #endif
50 }
51
52
53 /*
54  * Initialize the screen
55  */
56 void screen_new(void) {
57         send_ansi_detect();
58         look_for_ansi();
59         cls(0);
60         color(DIM_WHITE);
61 }
62
63
64 /*
65  * Beep.
66  */
67 void ctdl_beep(void) {
68         putc(7, stdout);
69 }
70
71
72 /*
73  * scr_printf() outputs to the terminal
74  */
75 int scr_printf(char *fmt, ...) {
76         static char outbuf[4096];       /* static for performance -- not re-entrant -- change if needed */
77         va_list ap;
78         int retval;
79         int i, len;
80
81         va_start(ap, fmt);
82         retval = vsnprintf(outbuf, sizeof outbuf, fmt, ap);
83         va_end(ap);
84
85         len = strlen(outbuf);
86         for (i = 0; i < len; ++i) {
87                 scr_putc(outbuf[i]);
88         }
89         return retval;
90 }
91
92
93 /*
94  * Read one character from the terminal
95  */
96 int scr_getc(int delay) {
97         unsigned char buf;
98
99         scr_flush();
100
101         buf = '\0';
102         if (!read(0, &buf, 1))
103                 logoff(NULL, 3);
104
105         lines_printed = 0;
106         return buf;
107 }
108
109
110 /*
111  * Issue the paginator prompt (more / hit any key to continue)
112  */
113 void hit_any_key(void) {
114         int a, b;
115
116         color(COLOR_PUSH);
117         color(DIM_RED);
118         scr_printf("%s\r", moreprompt);
119         color(COLOR_POP);
120         b = inkey();
121         for (a = 0; a < screenwidth; ++a) {
122                 scr_putc(' ');
123         }
124         scr_printf("\r");
125
126         if ((rc_prompt_control == 1) || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL))) {
127                 if (b == 'q' || b == 'Q' || b == 's' || b == 'S') {
128                         b = STOP_KEY;
129                 }
130                 if (b == 'n' || b == 'N') {
131                         b = NEXT_KEY;
132                 }
133         }
134
135         if (b == NEXT_KEY)
136                 sigcaught = SIGINT;
137         if (b == STOP_KEY)
138                 sigcaught = SIGQUIT;
139 }
140
141
142 /*
143  * Output one character to the terminal
144  */
145 int scr_putc(int c) {
146         /* handle tabs normally */
147         if (c == '\t') {
148                 do {
149                         scr_putc(' ');
150                 } while ((cols_printed % 8) != 0);
151                 return (c);
152         }
153
154         /* Output the character... */
155         if (putc(c, stdout) == EOF) {
156                 logoff(NULL, 3);
157         }
158
159         if (c == '\n') {
160                 ++lines_printed;
161                 cols_printed = 0;
162         }
163         else if (c == '\r') {
164                 cols_printed = 0;
165         }
166         else if (isprint(c)) {
167                 ++cols_printed;
168                 if ((screenwidth > 0) && (cols_printed > screenwidth)) {
169                         ++lines_printed;
170                         cols_printed = 0;
171                 }
172         }
173
174         /* How many lines output before stopping for the paginator?
175          * Depends on whether we are displaying a status line.
176          */
177         int height_offset = (((enable_color) && (screenwidth > 0) && (enable_status_line)) ? (3) : (2));
178
179         /* Ok, go check it.  Stop and display the paginator prompt if necessary. */
180         if ((screenheight > 0) && (lines_printed > (screenheight - height_offset))) {
181                 lines_printed = 0;
182                 hit_any_key();
183                 lines_printed = 0;
184                 cols_printed = 0;
185         }
186
187         return c;
188 }
189
190
191 void scr_flush(void) {
192         if ((enable_color) && (screenwidth > 0) && (enable_status_line)) {
193                 if (strlen(status_line) < screenwidth) {
194                         memset(&status_line[strlen(status_line)], 32, screenwidth - strlen(status_line));
195                 }
196                 printf("\033[s\033[1;1H\033[K\033[7m");
197                 fwrite(status_line, screenwidth, 1, stdout);
198                 printf("\033[27m\033[u");
199         }
200         fflush(stdout);
201 }
202
203
204 static volatile int caught_sigwinch = 0;
205
206
207 /*
208  * scr_winch() handles window size changes from SIGWINCH
209  * resizes all our windows for us
210  */
211 sighandler_t scr_winch(int signum) {
212         /* if we receive this signal, we must be running
213          * in a terminal that supports resizing.
214          */
215         caught_sigwinch = 1;
216         check_screen_dims();
217         signal(SIGWINCH, scr_winch);
218 }
219
220
221 /*
222  * Display a 3270-style "wait" indicator at the bottom of the screen
223  */
224 void scr_wait_indicator(int state) {
225         int sp = (screenwidth - 2);
226
227         if (!enable_status_line)
228                 return;
229
230         if (screenwidth > 0) {
231                 switch (state) {
232                 default:
233                 case 0: /* Idle */
234                         status_line[sp] = ' ';
235                         break;
236                 case 1: /* Waiting */
237                         status_line[sp] = 'X';
238                         break;
239                 case 2: /* Receiving */
240                         status_line[sp] = '<';
241                         break;
242                 case 3: /* Sending */
243                         status_line[sp] = '>';
244                         break;
245                 }
246                 scr_flush();
247         }
248 }