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