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