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