cd1d5263272491861e7621c6c11a6c55cd649131
[citadel.git] / 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         putc(7, stdout);
75 }
76         
77
78
79
80 /*
81  * scr_printf() outputs to the terminal
82  */
83 int scr_printf(char *fmt, ...)
84 {
85         static char outbuf[4096];       /* static for performance -- not re-entrant -- change if needed */
86         va_list ap;
87         int retval;
88         int i, len;
89
90         va_start(ap, fmt);
91         retval = vsnprintf(outbuf, sizeof outbuf, fmt, ap);
92         va_end(ap);
93
94         len = strlen(outbuf);
95         for (i=0; i<len; ++i) {
96                 scr_putc(outbuf[i]);
97         }
98         return retval;
99 }
100
101
102 /*
103  * Read one character from the terminal
104  */
105 int scr_getc(int delay)
106 {
107         unsigned char buf;
108
109         scr_flush();
110
111         buf = '\0';
112         if (!read (0, &buf, 1))
113                 logoff(NULL, 3);
114
115         lines_printed = 0;
116         return buf;
117 }
118
119 /*
120  * Issue the paginator prompt (more / hit any key to continue)
121  */
122 void hit_any_key(void) {
123         int a, b;
124
125         color(COLOR_PUSH);
126         color(DIM_RED);
127         scr_printf("%s\r", moreprompt);
128         color(COLOR_POP);
129         b=inkey();
130         for (a=0; a<screenwidth; ++a) {
131                 scr_putc(' ');
132         }
133         scr_printf("\r");
134
135         if ( (rc_prompt_control == 1) || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL)) ) {
136                 if (b == 'q' || b == 'Q' || b == 's' || b == 'S') {
137                         b = STOP_KEY;
138                 }
139                 if (b == 'n' || b == 'N') {
140                         b = NEXT_KEY;
141                 }
142         }
143
144         if (b==NEXT_KEY) sigcaught = SIGINT;
145         if (b==STOP_KEY) sigcaught = SIGQUIT;
146 }
147
148
149 /*
150  * Output one character to the terminal
151  */
152 int scr_putc(int c)
153 {
154         /* handle tabs normally */
155         if (c == '\t') {
156                 do {
157                         scr_putc(' ');
158                 } while ((cols_printed % 8) != 0);
159                 return(c);
160         }
161
162         /* Output the character... */
163         if (putc(c, stdout) == EOF) {
164                 logoff(NULL, 3);
165         }
166
167         if (c == '\n') {
168                 ++lines_printed;
169                 cols_printed = 0;
170         }
171         else if (c == '\r') {
172                 cols_printed = 0;
173         }
174         else if (isprint(c)) {
175                 ++cols_printed;
176                 if ((screenwidth > 0) && (cols_printed > screenwidth)) {
177                         ++lines_printed;
178                         cols_printed = 0;
179                 }
180         }
181
182         /* How many lines output before stopping for the paginator?
183          * Depends on whether we are displaying a status line.
184          */
185         int height_offset = ( ((enable_color) && (screenwidth > 0) && (enable_status_line)) ? (3) : (2) ) ;
186
187         /* Ok, go check it.  Stop and display the paginator prompt if necessary. */
188         if ((screenheight > 0) && (lines_printed > (screenheight-height_offset))) {
189                 lines_printed = 0;
190                 hit_any_key();
191                 lines_printed = 0;
192                 cols_printed = 0;
193         }
194
195         return c;
196 }
197
198 void scr_flush(void)
199 {
200         if ((enable_color) && (screenwidth > 0) && (enable_status_line)) {
201                 if (strlen(status_line) < screenwidth) {
202                         memset(&status_line[strlen(status_line)], 32, screenwidth - strlen(status_line));
203                 }
204                 printf("\033[s\033[1;1H\033[K\033[7m");
205                 fwrite(status_line, screenwidth, 1, stdout);
206                 printf("\033[27m\033[u");
207         }
208         fflush(stdout);
209 }
210
211
212 static volatile int caught_sigwinch = 0;
213
214
215 /*
216  * scr_winch() handles window size changes from SIGWINCH
217  * resizes all our windows for us
218  */
219 sighandler_t scr_winch(int signum)
220 {
221         /* if we receive this signal, we must be running
222          * in a terminal that supports resizing.
223          */
224         caught_sigwinch = 1;
225         check_screen_dims();
226         signal(SIGWINCH, scr_winch);
227 }
228
229
230
231 /*
232  * Display a 3270-style "wait" indicator at the bottom of the screen
233  */
234 void scr_wait_indicator(int state) {
235         int sp = (screenwidth - 2);
236
237         if (!enable_status_line) return;
238
239         if (screenwidth > 0) {
240                 switch (state) {
241                         default:
242                         case 0:  /* Idle */
243                                 status_line[sp] = ' ';
244                                 break;
245                         case 1:  /* Waiting */
246                                 status_line[sp] = 'X';
247                                 break;
248                         case 2:  /* Receiving */
249                                 status_line[sp] = '<';
250                                 break;
251                         case 3:  /* Sending */
252                                 status_line[sp] = '>';
253                                 break;
254                 }
255                 scr_flush();
256         }
257 }
258