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