0924261c99db5d237f660946c896acae00062bd9
[citadel.git] / citadel / textclient / screen.c
1 /* $Id$ */
2
3 /*
4  * Handle full-screen curses stuff
5  */
6
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <signal.h>
11 #include <string.h>
12 #include <stdarg.h>
13 #include <sys/types.h>
14 #include "sysdep.h"
15 #ifdef HAVE_VW_PRINTW
16 #define _vwprintw vw_printw
17 #else
18 /* SYSV style curses (Solaris, etc.) */
19 #define _vwprintw vwprintw
20 #endif
21 #ifndef HAVE_SNPRINTF
22 #include "snprintf.h"
23 #endif
24 #include <libcitadel.h>
25 #include "citadel.h"
26 #include "citadel_ipc.h"
27 #include "citadel_decls.h"
28 #include "commands.h"
29 #include "screen.h"
30
31 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
32 static SCREEN *myscreen = NULL;
33 static WINDOW *mainwindow = NULL;
34 static WINDOW *statuswindow = NULL;
35
36 char rc_screen;
37 #endif
38 char arg_screen;
39
40 extern int screenheight;
41 extern int screenwidth;
42 extern int rc_ansi_color;
43 extern void check_screen_dims(void);
44
45 void do_keepalive(void);
46
47
48 int is_curses_enabled(void) {
49 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
50         return mainwindow != NULL;
51 #else
52         return 0;
53 #endif
54 }
55
56 /*
57  * status_line() is a convenience function for writing a "typical"
58  * status line to the window.
59  */
60 void status_line(const char *humannode, const char *site_location,
61                  const char *room_name, int secure, int newmailcount)
62 {
63 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
64         if (statuswindow) {
65                 if (secure) {
66                         sln_printf("Encrypted ");
67                         waddch(statuswindow, ACS_VLINE);
68                         waddch(statuswindow, ' ');
69                 }
70                 if (room_name)
71                         sln_printf("%s on ", room_name);
72                 if (humannode)
73                         sln_printf("%s ", humannode);
74                 if (newmailcount > 0) {
75                         waddch(statuswindow, ACS_VLINE);
76                         sln_printf(" %d new mail ", newmailcount);
77                 }
78                 sln_printf("\n");
79         }
80 #endif /* HAVE_CURSES_H */
81 }
82
83
84 /*
85  * Display a 3270-style "wait" indicator at the bottom of the screen
86  */
87 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
88 void wait_indicator(int state) {
89
90         if (statuswindow && !isendwin()) {
91
92                 mvwinch(statuswindow, 0, screenwidth - 2);
93                 switch (state) {
94                 default:
95                 case 0:         /* Idle */
96                         waddch(statuswindow, ' ');
97                         break;
98                 case 1:         /* Waiting */
99                         waddch(statuswindow, 'X');
100                         break;
101                 case 2:         /* Receiving */
102                         waddch(statuswindow, '<');
103                         break;
104                 case 3:         /* Sending */
105                         waddch(statuswindow, '>');
106                         break;
107                 }
108                 waddch(statuswindow, '\r');
109                 wrefresh(statuswindow);
110                 wrefresh(mainwindow);   /* this puts the cursor back */
111         }
112 }
113 #else
114 void wait_indicator(int state) {}
115 #endif
116
117
118 /*
119  * Initialize the screen.  If newterm() fails, myscreen will be NULL and
120  * further handlers will assume we should be in line mode.
121  */
122 void screen_new(void)
123 {
124 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
125         if (arg_screen != RC_NO && rc_screen != RC_NO)
126                 myscreen = newterm(NULL, stdout, stdin);
127         if (myscreen) {
128                 cbreak();
129                 noecho();
130                 nonl();
131                 intrflush(stdscr, FALSE);
132                 keypad(stdscr, TRUE);
133                 /* Setup all our colors */
134                 start_color();
135                 if (rc_ansi_color)
136                         enable_color = 1;
137                 /*init_pair(DIM_BLACK, COLOR_BLACK, COLOR_BLACK);*/
138                 init_pair(DIM_RED, COLOR_RED, COLOR_BLACK);
139                 init_pair(DIM_GREEN, COLOR_GREEN, COLOR_BLACK);
140                 init_pair(DIM_YELLOW, COLOR_YELLOW, COLOR_BLACK);
141                 init_pair(DIM_BLUE, COLOR_BLUE, COLOR_BLACK);
142                 init_pair(DIM_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
143                 init_pair(DIM_CYAN, COLOR_CYAN, COLOR_BLACK);
144                 init_pair(DIM_WHITE, COLOR_WHITE, COLOR_BLACK);
145
146                 if (COLOR_PAIRS > 8)
147                         init_pair(8, COLOR_WHITE, COLOR_BLUE);
148         } else
149 #endif /* HAVE_CURSES_H */
150         {
151                 send_ansi_detect();
152                 look_for_ansi();
153                 cls(0);
154                 color(DIM_WHITE);
155         }
156         screen_set();
157         windows_new();
158 }
159
160
161 /*
162  * Kill the screen completely (used at exit).  It is safe to call this
163  * function more than once.
164  */
165 void screen_delete(void)
166 {
167         windows_delete();
168         screen_reset();
169 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
170         if (myscreen) {
171                 delscreen(myscreen);
172         }
173         myscreen = NULL;
174 #endif
175 }
176
177 /*
178  * Beep.
179  */
180 void ctdl_beep(void) {
181 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
182         if (myscreen)
183                 beep();
184         else
185 #endif
186         putc(7, stdout);
187 }
188         
189
190
191 /*
192  * Set screen/IO parameters, e.g. at start of program or return from external
193  * program run.
194  */
195 int screen_set(void)
196 {
197 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
198         if (myscreen) {
199                 set_term(myscreen);
200                 wrefresh(curscr);
201                 return 1;
202         }
203 #endif /* HAVE_CURSES_H */
204         return 0;
205 }
206
207
208 /*
209  * Reset screen/IO parameters, e.g. at exit or fork of external program.
210  */
211 int screen_reset(void)
212 {
213 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
214         if (myscreen) {
215                 if (!isendwin()) endwin();
216                 return 1;
217         }
218 #endif /* HAVE_CURSES_H */
219         return 0;
220 }
221
222
223 /*
224  * scr_printf() outputs to the main window (or screen if not in curses)
225  */
226 int scr_printf(char *fmt, ...)
227 {
228         va_list ap;
229         register int retval;
230
231         va_start(ap, fmt);
232 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
233         if (mainwindow) {
234                 retval = _vwprintw(mainwindow, fmt, ap);
235         } else
236 #endif
237                 retval = vprintf(fmt, ap);
238         va_end(ap);
239         return retval;
240 }
241
242
243 /*
244  * err_printf() outputs to error status window (or stderr if not in curses)
245  */
246 int err_printf(char *fmt, ...)
247 {
248         va_list ap;
249         register int retval;
250
251         va_start(ap, fmt);
252 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
253         if (mainwindow) {               /* FIXME: direct to error window */
254                 retval = _vwprintw(mainwindow, fmt, ap);
255                 if (fmt[strlen(fmt) - 1] == '\n')
256                         wrefresh(mainwindow);
257         } else
258 #endif
259                 retval = vfprintf(stderr, fmt, ap);
260         va_end(ap);
261         return retval;
262 }
263
264
265 /*
266  * sln_printf() outputs to error status window (or stderr if not in curses)
267  */
268 int sln_printf(char *fmt, ...)
269 {
270         va_list ap;
271         register int retval;
272 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
273         static char buf[4096];
274 #endif
275
276         va_start(ap, fmt);
277 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
278         if (statuswindow) {
279                 register char *i;
280                 
281                 retval = vsnprintf(buf, 4096, fmt, ap);
282                 for (i = buf; *i; i++) {
283                         if (*i == '\r' || *i == '\n')
284                                 wclrtoeol(statuswindow);
285                         sln_putc(*i);
286                         if (*i == '\r' || *i == '\n') {
287                                 wrefresh(statuswindow);
288                                 mvwinch(statuswindow, 0, 0);
289                         }
290                 }
291         } else
292 #endif
293                 retval = vprintf(fmt, ap);
294         va_end(ap);
295         return retval;
296 }
297
298
299 /*
300  * sln_printf_if() outputs to status window, no output if not in curses
301  */
302 int sln_printf_if(char *fmt, ...)
303 {
304         register int retval = 1;
305 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
306         static char buf[4096];
307         va_list ap;
308
309         va_start(ap, fmt);
310         if (statuswindow) {
311                 register char *i;
312                 
313                 retval = vsnprintf(buf, 4096, fmt, ap);
314                 for (i = buf; *i; i++) {
315                         if (*i == '\r' || *i == '\n')
316                                 wclrtoeol(statuswindow);
317                         sln_putc(*i);
318                         if (*i == '\r' || *i == '\n') {
319                                 wrefresh(statuswindow);
320                                 mvwinch(statuswindow, 0, 0);
321                         }
322                 }
323         }
324         va_end(ap);
325 #endif
326         return retval;
327 }
328
329
330 int scr_getc(int delay)
331 {
332   unsigned char buf;
333
334 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
335         if (mainwindow) {
336                 wtimeout(mainwindow, delay);
337                 return wgetch(mainwindow);
338         }
339 #endif
340
341         buf = '\0';
342         if (!read (0, &buf, 1))
343                 logoff(NULL, 3);
344         return buf;
345 }
346
347 /* the following is unused and looks broken, but there may
348    be some input problems still lurking in curses mode, so
349    i'll leave it blocked out for now for informational
350    purposes. */
351 #if 0
352 int scr_blockread(void)
353   {
354     int a = 0;
355 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
356     wtimeout(mainwindow, S_KEEPALIVE); 
357     while (1)
358       {
359         do_keepalive();
360         a = wgetch(mainwindow); /* will block for food */
361         if (a != ERR)
362           break;
363         /* a = scr_getc(); */
364       }
365 #endif
366     return a;
367   }
368 #endif /* 0 */
369
370 /*
371  * scr_putc() outputs a single character
372  */
373 int scr_putc(int c)
374 {
375 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
376         if (mainwindow) {
377                 if (c == 7) beep();
378                 if (waddch(mainwindow, c) != OK)
379                         logoff(NULL, 3);
380                 return c;
381         }
382 #endif
383         if (putc(c, stdout) == EOF)
384                 logoff(NULL, 3);
385         return c;
386 }
387
388
389 int sln_putc(int c)
390 {
391 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
392         if (statuswindow)
393                 return ((waddch(statuswindow, c) == OK) ? c : EOF);
394 #endif
395         return putc(c, stdout);
396 }
397
398
399 int sln_putc_if(int c)
400 {
401 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
402         if (statuswindow)
403                 return ((waddch(statuswindow, c) == OK) ? c : EOF);
404 #endif
405         return 1;
406 }
407
408
409 /*
410  * scr_color() sets the window color for mainwindow
411  */
412 int scr_color(int colornum)
413 {
414 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
415         if (mainwindow) {
416 #ifdef HAVE_WCOLOR_SET
417                 wcolor_set(mainwindow, (colornum & 7), NULL);
418 #else
419                 wattron(mainwindow, COLOR_PAIR((colornum & 7)));
420 #endif
421                 if (colornum & 8) {
422                         wattron(mainwindow, A_BOLD);
423                 } else {
424                         wattroff(mainwindow, A_BOLD);
425                 }
426                 return 1;
427         }
428 #endif
429         return 0;
430 }
431
432
433 void scr_flush(void)
434 {
435 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
436         if (mainwindow)
437                 wrefresh(mainwindow);
438         else
439 #endif
440                 fflush(stdout);
441 }
442
443
444 void err_flush(void)
445 {
446 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
447         if (mainwindow)         /* FIXME: error status window needed */
448                 wrefresh(mainwindow);
449         else
450 #endif
451                 fflush(stderr);
452 }
453
454
455 void sln_flush(void)
456 {
457 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
458         if (statuswindow)
459                 wrefresh(statuswindow);
460         else
461 #endif
462                 fflush(stdout);
463 }
464
465 static volatile int caught_sigwinch = 0;
466
467 /*
468  * this is not supposed to be called from a signal handler.
469  */
470 int scr_set_windowsize(CtdlIPC* ipc)
471 {
472 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
473         if (mainwindow && caught_sigwinch) {
474                 caught_sigwinch = 0;
475 #ifdef HAVE_RESIZETERM
476                 resizeterm(screenheight + 1, screenwidth);
477 #endif
478 #ifdef HAVE_WRESIZE
479                 wresize(mainwindow, screenheight, screenwidth);
480                 wresize(statuswindow, 1, screenwidth);
481 #endif
482                 mvwin(statuswindow, screenheight, 0);
483                 status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
484                                 room_name, secure, -1);
485                 wnoutrefresh(mainwindow);
486                 wnoutrefresh(statuswindow);
487                 doupdate();
488                 return 1;
489         }
490 #endif /* HAVE_CURSES_H */
491         return 0;
492 }
493
494 /*
495  * scr_winch() handles window size changes from SIGWINCH
496  * resizes all our windows for us
497  */
498 RETSIGTYPE scr_winch(int signum)
499 {
500         /* if we receive this signal, we must be running
501            in a terminal that supports resizing. */
502         have_xterm = 1;
503         caught_sigwinch = 1;
504         check_screen_dims();
505         signal(SIGWINCH, scr_winch);
506 }
507
508
509 /*
510  * Initialize the window(s) we will be using.
511  */
512 void windows_new(void)
513 {
514 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
515         register int x, y;
516
517         if (myscreen) {
518                 getmaxyx(stdscr, y, x);
519                 mainwindow = newwin(y - 1, x, 0, 0);
520                 screenwidth = x;
521                 screenheight = y - 1;
522                 immedok(mainwindow, FALSE);
523                 leaveok(mainwindow, FALSE);
524                 scrollok(mainwindow, TRUE);
525                 statuswindow = newwin(1, x, y - 1, 0);
526
527                 if (COLOR_PAIRS > 8)
528                         wbkgdset(statuswindow, ' ' | COLOR_PAIR(8));
529                 else
530                         wbkgdset(statuswindow, ' ' | COLOR_PAIR(DIM_WHITE));
531
532                 werase(statuswindow);
533                 immedok(statuswindow, FALSE);
534                 leaveok(statuswindow, FALSE);
535                 scrollok(statuswindow, FALSE);
536                 wrefresh(statuswindow);
537         }
538 #else /* HAVE_CURSES_H */
539
540 #endif /* HAVE_CURSES_H */
541 }
542
543
544 /*
545  * Deinitialize the window(s) we were using (at exit).
546  */
547 void windows_delete(void)
548 {
549 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
550         if (mainwindow)
551                 delwin(mainwindow);
552         mainwindow = NULL;
553         if (statuswindow)
554                 delwin(statuswindow);
555         statuswindow = NULL;
556 #else /* HAVE_CURSES_H */
557
558 #endif /* HAVE_CURSES_H */
559 }