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