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