tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

screen.c
Go to the documentation of this file.
1/*
2 * Project : tin - a Usenet reader
3 * Module : screen.c
4 * Author : I. Lea & R. Skrenta
5 * Created : 1991-04-01
6 * Updated : 2021-09-28
7 * Notes :
8 *
9 * Copyright (c) 1991-2022 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright notice,
17 * this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 *
23 * 3. Neither the name of the copyright holder nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40
41#ifndef TIN_H
42# include "tin.h"
43#endif /* !TIN_H */
44#ifndef TCURSES_H
45# include "tcurses.h"
46#endif /* !TCURSES_H */
47
49
50#ifndef USE_CURSES
52#endif /* !USE_CURSES */
53
54
55/*
56 * Move the cursor to the lower-left of the screen, where it won't be annoying
57 */
58void
60 void)
61{
62 if (!cmd_line)
64}
65
66
67/*
68 * helper for the various *_message() functions
69 * returns a pointer to an allocated buffer with the formatted message
70 * must be freed if not needed anymore
71 */
72char *
74 const char *fmt,
75 va_list ap)
76{
77 size_t size = LEN;
78 char *msg = my_malloc(size);
79 int used;
80 va_list aq;
81
82 while (1) {
83 begin_va_copy(aq, ap);
84 used = vsnprintf(msg, size, fmt, aq);
85 end_va_copy(aq);
86
87 if (used >= 0 && used < (int) size)
88 break;
89
90 size <<= 1;
91 msg = my_realloc(msg, size);
92 }
93
94 return msg;
95}
96
97
98/*
99 * Centre a formatted colour message at the bottom of the screen
100 */
101void
103 const char *fmt,
104 ...)
105{
106 char *buf;
107 va_list ap;
108
109 va_start(ap, fmt);
110
112#ifdef HAVE_COLOR
113 fcol(tinrc.col_message);
114#endif /* HAVE_COLOR */
115
116 buf = fmt_message(fmt, ap);
117 center_line(cLINES, FALSE, buf); /* center the message at screen bottom */
118 free(buf);
119
120#ifdef HAVE_COLOR
121 fcol(tinrc.col_normal);
122#endif /* HAVE_COLOR */
123 stow_cursor();
124
125 va_end(ap);
126}
127
128
129/*
130 * Print a formatted colour message at the bottom of the screen, wait a while
131 */
132void
134 unsigned int sdelay,
135 const char *fmt,
136 ...)
137{
138 char *buf, *msg;
139 va_list ap;
140
141 va_start(ap, fmt);
142
144#ifdef HAVE_COLOR
145 fcol(tinrc.col_message);
146#endif /* HAVE_COLOR */
147
148 buf = fmt_message(fmt, ap);
149 /* test for multiline messages */
150 if (strrchr(buf, '\n')) {
151 char *from, *to;
152
153 for (from = buf; *from && (to = strchr(from, '\n')); from = ++to) {
154 *to = '\0';
155 msg = strunc(from, cCOLS - 1);
156 my_fputs(msg, stdout);
157 my_fputc('\n', stdout);
158 free(msg);
159 }
160 } else {
161 msg = strunc(buf, cCOLS - 1);
162 my_fputs(msg, stdout);
163 free(msg);
164 }
165 free(buf);
166
167#ifdef HAVE_COLOR
168 fcol(tinrc.col_normal);
169#endif /* HAVE_COLOR */
170 cursoron();
171 my_flush();
172 va_end(ap);
173
174#ifdef HAVE_SELECT
175 /* allow to skip sleep time via key-press */
176 {
177 int nfds;
178 fd_set readfds;
179 struct timeval tv;
180
181 forever {
182 FD_ZERO(&readfds);
183 FD_SET(STDIN_FILENO, &readfds);
184 tv.tv_sec = sdelay;
185 tv.tv_usec = 0;
186
187# ifdef HAVE_SELECT_INTP
188 nfds = select(STDIN_FILENO + 1, (int *) &readfds, NULL, NULL, &tv);
189# else
190 nfds = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
191# endif /* HAVE_SELECT_INTP */
192 if (nfds == -1) {
193
194 if (errno != EINTR) {
195 perror_message("wait_message(select()) failed");
196 free(tin_progname);
197 giveup();
198 } else
199 return;
200 } else
201 break;
202 }
203
204 if (nfds > 0) {
205 if (FD_ISSET(STDIN_FILENO, &readfds))
206# if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
207 ReadWch();
208# else
209 ReadCh();
210# endif /* MULTIBYTE_ABLE && !NO_LOCALE */
211 }
212 }
213#else
214 (void) sleep(sdelay);
215#endif /* HAVE_SELECT */
216}
217
218
219/*
220 * Print a formatted message to stderr, no colour is added.
221 * Interesting - this function implicitly clears 'errno'
222 */
223void
225 unsigned int sdelay,
226 const char *fmt,
227 ...)
228{
229 char *buf;
230 va_list ap;
231
232 va_start(ap, fmt);
233
234 errno = 0;
236
237 buf = fmt_message(fmt, ap);
238 my_fputs(buf, stderr); /* don't use my_fprintf() here due to %format chars */
239 my_fflush(stderr);
240 free(buf);
241
242 if (cmd_line) {
243 my_fputc('\n', stderr);
244 fflush(stderr);
245 } else {
246 stow_cursor();
247 (void) sleep(sdelay);
249 }
250
251 va_end(ap);
252}
253
254
255/*
256 * Print a formatted error message to stderr, no colour is added.
257 * This function implicitly clears 'errno'
258 */
259void
261 const char *fmt,
262 ...)
263{
264 char *buf;
265 int err;
266 va_list ap;
267
268 err = errno;
269 va_start(ap, fmt);
270
272
273 if ((buf = fmt_message(fmt, ap)) != NULL) {
274 error_message(2, "%s: Error: %s", buf, strerror(err));
275 free(buf);
276 }
277
278 va_end(ap);
279}
280
281
282void
284 void)
285{
286 if (!cmd_line) {
287 MoveCursor(cLINES, 0);
288 CleartoEOLN();
289 cursoroff();
290#ifndef USE_CURSES
291 my_flush();
292#endif /* !USE_CURSES */
293 }
294}
295
296
297void
299 int line,
300 t_bool inverse,
301 const char *str)
302{
303 char *ln;
304 int pos;
305 int len;
306
307 len = strwidth(str);
308
309#if defined(HAVE_LIBICUUC) && defined(MULTIBYTE_ABLE) && defined(HAVE_UNICODE_UBIDI_H) && !defined(NO_LOCALE)
310 if (tinrc.render_bidi && IS_LOCAL_CHARSET("UTF-8") && len > 1) {
311 t_bool is_rtl;
312
313 if ((ln = render_bidi(str, &is_rtl)) == NULL)
314 ln = my_strdup(str);
315 } else
316#endif /* HAVE_LIBICUUC && MULTIBYTE_ABLE && HAVE_UNICODE_UBIDI_H && !NO_LOCALE */
317 ln = my_strdup(str);
318
319 if (!cmd_line) {
320 if (cCOLS >= len)
321 pos = (cCOLS - len) / 2;
322 else
323 pos = 1;
324
325 MoveCursor(line, pos);
326 if (inverse) {
327 StartInverse();
328 my_flush();
329 }
330 }
331
332 if (len >= cCOLS) {
333 char *buffer;
334
335 buffer = strunc(ln, cCOLS - 2);
336 my_fputs(buffer, stdout);
337 free(buffer);
338 } else
339 my_fputs(ln, stdout);
340
341 if (cmd_line)
342 my_flush();
343 else {
344 if (inverse)
345 EndInverse();
346 }
347 free(ln);
348}
349
350
351void
353 int line)
354{
355#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
356 wchar_t *wtmp;
357#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
358
359 MoveCursor(line, 0);
360
361 if (tinrc.draw_arrow) {
362#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
363 if (tinrc.utf8_graphics) {
364 my_fputwc(CURSOR_HORIZ, stdout);
365 my_fputwc(CURSOR_ARROW, stdout);
366 } else
367#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
368 my_fputs("->", stdout);
369 } else {
370#ifdef USE_CURSES
371 char buffer[BUFSIZ];
372 char *s = screen_contents(line, 0, buffer);
373#else
374 char *s = screen[line - INDEX_TOP].col;
375#endif /* USE_CURSES */
376#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
377 if ((wtmp = char2wchar_t(s)) != NULL) {
378 StartInverse();
379 my_fputws(wtmp, stdout);
380 EndInverse();
383/* EndInverse(); */ /* needed? */
384 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
385 }
386 free(wtmp);
387 }
388#else
389 StartInverse();
390 my_fputs(s, stdout);
391 EndInverse();
393 MoveCursor(line, mark_offset);
394/* EndInverse(); */ /* needed? */
395 my_fputc(s[mark_offset], stdout);
396 }
397#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
398 }
399 stow_cursor();
400}
401
402
403void
405 void)
406{
407 int line = INDEX_TOP + currmenu->curr - currmenu->first;
408#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
409 wchar_t *wtmp;
410#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
411
412 if (!currmenu->max)
413 return;
414
415 MoveCursor(line, 0);
416
417 if (tinrc.draw_arrow)
418 my_fputs(" ", stdout);
419 else {
420#ifdef USE_CURSES
421 char buffer[BUFSIZ];
422 char *s = screen_contents(line, 0, buffer);
423#else
424 char *s;
425
426 if (line - INDEX_TOP < 0) /* avoid underruns */
427 line = INDEX_TOP;
428
429 s = screen[line - INDEX_TOP].col;
430#endif /* USE_CURSES */
431 EndInverse();
432#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
433 if ((wtmp = char2wchar_t(s)) != NULL) {
434 my_fputws(wtmp, stdout);
437 StartInverse();
438 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
439 EndInverse();
440 }
441 free(wtmp);
442 }
443#else
444 my_fputs(s, stdout);
446 MoveCursor(line, mark_offset);
447 StartInverse();
448 my_fputc(s[mark_offset], stdout);
449 EndInverse();
450 }
451#endif /* MULTIBYTE_ABLE && !NOLOCALE */
452 }
453}
454
455
456void
458 const char *title)
459{
460 int col;
461
463 if (col > 0) {
464 MoveCursor(0, col);
465#ifdef HAVE_COLOR
466 fcol(tinrc.col_title);
467#endif /* HAVE_COLOR */
468 /* you have mail message in */
470
471#ifdef HAVE_COLOR
472 fcol(tinrc.col_normal);
473#endif /* HAVE_COLOR */
474 }
475 center_line(0, TRUE, title); /* wastes some space on the left */
476}
477
478
479void
481 void)
482{
483#ifdef USE_CURSES
484 if (!cmd_line)
485 beep();
486 else {
487#endif /* USE_CURSES */
488 my_fputc('\007', stdout);
489 my_flush();
490#ifdef USE_CURSES
491 }
492#endif /* USE_CURSES */
493}
494
495
496void
498 void)
499{
500 static const char buf[] = "|/-\\|/-\\ "; /* don't remove the tailing space! */
501 static unsigned short int i = 0;
502
503 if (batch_mode)
504 return;
505
506 if (i > 7)
507 i = 0;
508
509#ifdef HAVE_COLOR
510 fcol(tinrc.col_message);
511#endif /* HAVE_COLOR */
512 my_printf("\b%c", buf[i++]);
513 my_flush();
514#ifdef HAVE_COLOR
515 fcol(tinrc.col_normal);
516#endif /* HAVE_COLOR */
517}
518
519
520#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
521# define DISPLAY_FMT "%s %3d%% "
522#else
523# define DISPLAY_FMT "%s %3d%%"
524#endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
525/*
526 * progressmeter in %
527 */
528void
530 const char *txt,
531 t_artnum count,
532 t_artnum total)
533{
534 char display[LEN];
535 int ratio;
536 time_t curr_time;
537 static char last_display[LEN];
538 static int last_ratio;
539 static t_artnum last_count;
540 static t_artnum last_total;
541 static time_t last_update;
542#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
543 static int average;
544 static int samples;
545 static int last_secs_left;
546 static int sum;
547 char *display_format;
548 int time_diff;
549 int secs_left;
550 t_artnum count_diff;
551 static struct t_tintime last_time;
552 static struct t_tintime this_time;
553#endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
554
555 if (batch_mode || count <= 0 || total <= 1)
556 return;
557
558 /* If this is a new progress meter, start recalculating */
559 if ((last_total != total) || (count <= last_count)) {
560 last_ratio = -1;
561 last_display[0] = '\0';
562 last_update = time(NULL) - 2;
563 }
564
565 curr_time = time(NULL);
566 ratio = (int) ((count * 100) / total);
567 if ((ratio == last_ratio) && (curr_time - last_update < 2))
568 /*
569 * return if ratio did not change and less than
570 * 2 seconds since last update to reduce output
571 */
572 return;
573
574 last_update = curr_time;
575
576#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
577 display_format = my_malloc(strlen(DISPLAY_FMT) + strlen(_(txt_remaining)) + 1);
578 strcpy(display_format, DISPLAY_FMT);
579
580 if (last_ratio == -1) {
581 /* Don't print the time remaining */
582 snprintf(display, sizeof(display), display_format, txt, ratio);
583
584 /* Reset the variables */
585 sum = average = samples = last_secs_left = 0;
586 } else {
587 /* Get the current time */
588 tin_gettime(&this_time);
589 time_diff = (int) ((this_time.tv_sec - last_time.tv_sec) * 1000000);
590 time_diff = (int) (time_diff + ((this_time.tv_nsec - last_time.tv_nsec) / 1000));
591 count_diff = (count - last_count);
592
593 if (!count_diff) /* avoid div by zero */
594 count_diff++;
595
596 /*
597 * Calculate a running average based on the last 20 samples. For the
598 * first 19 samples just add all and divide by the number of samples.
599 * From the 20th sample on use only the last 20 samples to calculate
600 * the running averave. To make things easier we don't want to store
601 * and keep track of all of them, so we assume that the first sample
602 * was close to the current average and subtract it from sum. Then,
603 * the new sample is added to the sum and the sum is divided by 20 to
604 * get the new average.
605 */
606 if (samples == 20) {
607 sum -= average;
608 sum = (int) (sum + (time_diff / count_diff));
609 average = sum / 20;
610 } else {
611 sum = (int) (sum + (time_diff / count_diff));
612 average = sum / ++samples;
613 }
614
615 if (average >= 1000000)
616 secs_left = (int) ((total - count) * (average / 1000000));
617 else
618 secs_left = (int) (((total - count) * average) / 1000000);
619
620 if (secs_left < 0)
621 secs_left = 0;
622
623 if ((secs_left > 0) && (last_secs_left == 0))
624 last_secs_left = secs_left;
625
626 if (samples < 5)
627 /* Don't print the time remaining */
628 snprintf(display, sizeof(display), display_format, txt, ratio);
629 else {
630 /* Don't allow time remaining to increase by 1 or 2 seconds */
631 if ((secs_left == last_secs_left + 1) || (secs_left == last_secs_left + 2))
632 secs_left = last_secs_left;
633 else if (secs_left < last_secs_left)
634 last_secs_left = secs_left;
635 strcat(display_format, _(txt_remaining));
636 snprintf(display, sizeof(display), display_format, txt, ratio, secs_left / 60, secs_left % 60);
637 }
638 }
639 free(display_format);
640
641 tin_gettime(&last_time);
642#else
643 snprintf(display, sizeof(display), DISPLAY_FMT, txt, ratio);
644#endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
645
646 /* Only display text if it changed from last time */
647 if (strcmp(display, last_display)) {
648 char *tmp;
649
650 if (RawState()) {
652 MoveCursor(cLINES, 0);
653 } else {
654 my_printf("\r");
655 my_flush();
656 CleartoEOLN();
657 }
658
659#ifdef HAVE_COLOR
660 if (RawState())
661 fcol(tinrc.col_message);
662#endif /* HAVE_COLOR */
663
664 /*
665 * TODO: depending on the length of the newsgroup name
666 * it's possible to cut away a great part of the progress meter
667 * perhaps we should shorten the newsgroup name instead?
668 */
669 my_printf("%s", sized_message(&tmp, "%s", display));
670 free(tmp);
671
672#ifdef HAVE_COLOR
673 if (RawState())
674 fcol(tinrc.col_normal);
675#endif /* HAVE_COLOR */
676
677#ifndef USE_CURSES
678 if (!RawState())
679 MoveCursor(cLINES, 0);
680#endif /* !USE_CURSES */
681
682 my_flush();
683 STRCPY(last_display, display);
684 }
685
686 last_count = count;
687 last_total = total;
688 last_ratio = ratio;
689}
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
char mailbox[PATH_LEN]
Definition: init.c:91
t_bool cmd_line
Definition: init.c:129
int art_mark_width
Definition: init.c:118
constext txt_you_have_mail[]
Definition: lang.c:1005
t_menu * currmenu
Definition: init.c:166
constext txt_type_h_for_help[]
Definition: lang.c:908
int cCOLS
Definition: curses.c:53
int cLINES
Definition: curses.c:52
char * tin_progname
Definition: init.c:105
struct t_config tinrc
Definition: init.c:192
t_bool batch_mode
Definition: init.c:127
static char buf[16]
Definition: langinfo.c:50
int errno
int ReadCh(void)
Definition: curses.c:1110
void StartInverse(void)
Definition: curses.c:540
int RawState(void)
Definition: curses.c:604
void cursoroff(void)
Definition: curses.c:721
t_bool mail_check(const char *mailbox_name)
Definition: misc.c:903
int tin_gettime(struct t_tintime *tt)
Definition: misc.c:4201
char * my_strdup(const char *str)
Definition: string.c:139
void MoveCursor(int row, int col)
Definition: curses.c:441
int strwidth(const char *str)
Definition: string.c:1050
void cursoron(void)
Definition: curses.c:712
char * sized_message(char **result, const char *format, const char *subject)
Definition: prompt.c:675
char * strunc(const char *message, int len)
Definition: string.c:1076
void CleartoEOLN(void)
Definition: curses.c:458
#define strerror(n)
Definition: proto.h:700
_Noreturn void giveup(void)
Definition: main.c:1058
void EndInverse(void)
Definition: curses.c:564
void draw_arrow_mark(int line)
Definition: screen.c:352
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:298
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
void perror_message(const char *fmt,...)
Definition: screen.c:260
void info_message(const char *fmt,...)
Definition: screen.c:102
void spin_cursor(void)
Definition: screen.c:497
void erase_arrow(void)
Definition: screen.c:404
void ring_bell(void)
Definition: screen.c:480
int mark_offset
Definition: screen.c:48
void clear_message(void)
Definition: screen.c:283
#define DISPLAY_FMT
Definition: screen.c:523
char * fmt_message(const char *fmt, va_list ap)
Definition: screen.c:73
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void stow_cursor(void)
Definition: screen.c:59
void show_title(const char *title)
Definition: screen.c:457
struct t_screen * screen
Definition: screen.c:51
void show_progress(const char *txt, t_artnum count, t_artnum total)
Definition: screen.c:529
t_bool draw_arrow
Definition: tinrc.h:223
char art_marked_selected
Definition: tinrc.h:73
int curr
Definition: tin.h:2056
int first
Definition: tin.h:2058
int max
Definition: tin.h:2057
Definition: tin.h:1973
char * col
Definition: tin.h:1974
time_t tv_sec
Definition: tin.h:2085
long tv_nsec
Definition: tin.h:2086
#define my_flush()
Definition: tcurses.h:177
#define my_fflush(stream)
Definition: tcurses.h:178
#define my_fputs(str, stream)
Definition: tcurses.h:159
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
#define vsnprintf
Definition: tin.h:2467
#define LEN
Definition: tin.h:860
long t_artnum
Definition: tin.h:229
#define IS_LOCAL_CHARSET(c)
Definition: tin.h:782
#define STRCPY(dst, src)
Definition: tin.h:820
#define my_malloc(size)
Definition: tin.h:2245
#define end_va_copy(dst)
Definition: tin.h:2456
#define _(Text)
Definition: tin.h:94
#define forever
Definition: tin.h:816
#define snprintf
Definition: tin.h:2464
#define begin_va_copy(dst, src)
Definition: tin.h:2455
#define INDEX_TOP
Definition: tin.h:1019
#define my_realloc(ptr, size)
Definition: tin.h:2247
#define STDIN_FILENO
Definition: tin.h:394