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