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