ncurses 5.6 - patch 20070128
[ncurses.git] / tack / sync.c
1 /*
2 ** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3 ** 
4 ** This file is part of TACK.
5 ** 
6 ** TACK is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2, or (at your option)
9 ** any later version.
10 ** 
11 ** TACK is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ** GNU General Public License for more details.
15 ** 
16 ** You should have received a copy of the GNU General Public License
17 ** along with TACK; see the file COPYING.  If not, write to
18 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 ** Boston, MA 02110-1301, USA
20 */
21
22 #include <tack.h>
23 #include <time.h>
24
25 MODULE_ID("$Id: sync.c,v 1.9 2006/05/06 20:45:48 tom Exp $")
26
27 /* terminal-synchronization and performance tests */
28
29 static void sync_home(struct test_list *, int *, int *);
30 static void sync_lines(struct test_list *, int *, int *);
31 static void sync_clear(struct test_list *, int *, int *);
32 static void sync_summary(struct test_list *, int *, int *);
33
34 static struct test_list sync_test_list[] = {
35         {MENU_NEXT, 0, 0, 0, "b) baud rate test", sync_home, 0},
36         {MENU_NEXT, 0, 0, 0, "l) scroll performance", sync_lines, 0},
37         {MENU_NEXT, 0, 0, 0, "c) clear screen performance", sync_clear, 0},
38         {MENU_NEXT, 0, 0, 0, "p) summary of results", sync_summary, 0},
39         {0, 0, 0, 0, txt_longer_test_time, longer_test_time, 0},
40         {0, 0, 0, 0, txt_shorter_test_time, shorter_test_time, 0},
41         {MENU_LAST, 0, 0, 0, 0, 0, 0}
42 };
43
44 struct test_menu sync_menu = {
45         0, 'n', 0,
46         "Performance tests", "perf", "n) run standard tests",
47         sync_test, sync_test_list, 0, 0, 0
48 };
49
50 int tty_can_sync;               /* TRUE if tty_sync_error() returned FALSE */
51 static int tty_newline_rate;    /* The number of newlines per second */
52 static int tty_clear_rate;      /* The number of clear-screens per second */
53 unsigned long tty_cps;          /* The number of characters per second */
54
55 #define TTY_ACK_SIZE 64
56
57 static int ACK_terminator;      /* terminating ACK character */
58 static int ACK_length;          /* length of ACK string */
59 static const char *tty_ENQ;     /* enquire string */
60 static char tty_ACK[TTY_ACK_SIZE]; /* ACK response, set by tty_sync_error() */
61
62 /*****************************************************************************
63  *
64  * Terminal synchronization.
65  *
66  *      These functions handle the messy business of enq-ack handshaking
67  *      for timing purposes.
68  *
69  *****************************************************************************/
70
71 int
72 tty_sync_error(void)
73 {
74         int ch, trouble, ack;
75
76         trouble = FALSE;
77         for (;;) {
78                 tt_putp(tty_ENQ);       /* send ENQ */
79                 ch = getnext(STRIP_PARITY);
80                 event_start(TIME_SYNC); /* start the timer */
81
82                 /*
83                    The timer doesn't start until we get the first character.
84                    After that I expect to get the remaining characters of
85                    the acknowledge string in a short period of time.  If
86                    that is not true then these characters are coming from
87                    the user and we need to send the ENQ sequence out again.
88                 */
89                 for (ack = 0; ; ) {
90                         if (ack < TTY_ACK_SIZE - 2) {
91                                 tty_ACK[ack] = ch;
92                                 tty_ACK[ack + 1] = '\0';
93                         }
94                         if (ch == ACK_terminator) {
95                                 return trouble;
96                         }
97                         if (++ack >= ACK_length) {
98                                 return trouble;
99                         }
100                         ch = getnext(STRIP_PARITY);
101                         if (event_time(TIME_SYNC) > 400000) {
102                                 break;
103                         }
104                 }
105
106                 set_attr(0);    /* just in case */
107                 put_crlf();
108                 if (trouble) {
109                         /* The terminal won't sync.  Life is not good. */
110                         return TRUE;
111                 }
112                 put_str(" -- sync -- ");
113                 trouble = TRUE;
114         }
115 }
116
117 /*
118 **      flush_input()
119 **
120 **      Throw away any output.
121 */
122 void 
123 flush_input(void)
124 {
125         if (tty_can_sync == SYNC_TESTED && ACK_terminator >= 0) {
126                 (void) tty_sync_error();
127         } else {
128                 spin_flush();
129         }
130 }
131
132 /*
133 **      probe_enq_ok()
134 **
135 **      does the terminal do enq/ack handshaking?
136 */
137 static void 
138 probe_enq_ok(void)
139 {
140         int tc, len, ulen;
141
142         put_str("Testing ENQ/ACK, standby...");
143         fflush(stdout);
144         can_test("u8 u9", FLAG_TESTED);
145
146 #ifdef user9
147         tty_ENQ = user9 ? user9 : "\005";
148 #else
149         tty_ENQ = "\005";
150 #endif
151         tc_putp(tty_ENQ);
152         event_start(TIME_SYNC); /* start the timer */
153         read_key(tty_ACK, TTY_ACK_SIZE - 1);
154
155         if (event_time(TIME_SYNC) > 400000 || tty_ACK[0] == '\0') {
156                 /* These characters came from the user.  Sigh. */
157                 tty_can_sync = SYNC_FAILED;
158                 ptext("\nThis program expects the ENQ sequence to be");
159                 ptext(" answered with the ACK character.  This will help");
160                 ptext(" the program reestablish synchronization when");
161                 ptextln(" the terminal is overrun with data.");
162                 ptext("\nENQ sequence from (u9): ");
163                 putln(expand(tty_ENQ));
164                 ptext("ACK received: ");
165                 putln(expand(tty_ACK));
166 #ifdef user8
167                 len = user8 ? strlen(user8) : 0;
168 #else
169                 len = 0;
170 #endif
171                 sprintf(temp, "Length of ACK %d.  Expected length of ACK %d.",
172                         (int) strlen(tty_ACK), len);
173                 ptextln(temp);
174 #ifdef user8
175                 if (len) {
176                         temp[0] = user8[len - 1];
177                         temp[1] = '\0';
178                         ptext("Terminating character found in (u8): ");
179                         putln(expand(temp));
180                 }
181 #endif
182                 return;
183         }
184
185         tty_can_sync = SYNC_TESTED;
186         if ((len = strlen(tty_ACK)) == 1) {
187                 /* single character acknowledge string */
188                 ACK_terminator = tty_ACK[0];
189                 ACK_length = 4096;
190                 return;
191         }
192         tc = tty_ACK[len - 1];
193 #ifdef user8
194         if (user8) {
195                 ulen = strlen(user8);
196                 if (tc == user8[ulen - 1]) {
197                         /* ANSI style acknowledge string */
198                         ACK_terminator = tc;
199                         ACK_length = 4096;
200                         return;
201                 }
202         }
203 #endif
204         /* fixed length acknowledge string */
205         ACK_length = len;
206         ACK_terminator = -2;
207 }
208
209 /*
210 **      verify_time()
211 **
212 **      verify that the time tests are ready to run.
213 **      If the baud rate is not set then compute it.
214 */
215 void
216 verify_time(void)
217 {
218         int status, ch;
219
220         if (tty_can_sync == SYNC_FAILED) {
221                 return;
222         }
223         probe_enq_ok();
224         put_crlf();
225         if (tty_can_sync == SYNC_TESTED) {
226                 put_crlf();
227                 if (ACK_terminator >= 0) {
228                         ptext("ACK terminating character: ");
229                         temp[0] = ACK_terminator;
230                         temp[1] = '\0';
231                         ptextln(expand(temp));
232                 } else {
233                         sprintf(temp, "Fixed length ACK, %d characters",
234                                 ACK_length);
235                         ptextln(temp);
236                 }
237         }
238         if (tty_baud_rate == 0) {
239                 sync_home(&sync_test_list[0], &status, &ch);
240         }
241 }
242
243 /*****************************************************************************
244  *
245  * Terminal performance tests
246  *
247  *      Find out how fast the terminal can:
248  *        1) accept characters
249  *        2) scroll the screen
250  *        3) clear the screen
251  *
252  *****************************************************************************/
253
254 /*
255 **      sync_home(test_list, status, ch)
256 **
257 **      Baudrate test
258 */
259 static void
260 sync_home(
261         struct test_list *t,
262         int *state,
263         int *ch)
264 {
265         int j, k;
266         unsigned long rate;
267
268         if (!cursor_home && !cursor_address && !row_address) {
269                 ptext("Terminal can not home cursor.  ");
270                 generic_done_message(t, state, ch);
271                 return;
272         }
273         if (skip_pad_test(t, state, ch,
274                 "(home) Start baudrate search")) {
275                 return;
276         }
277         pad_test_startup(1);
278         do {
279                 go_home();
280                 for (j = 1; j < lines; j++) {
281                         for (k = 0; k < columns; k++) {
282                                 if (k & 0xF) {
283                                         put_this(letter);
284                                 } else {
285                                         put_this('.');
286                                 }
287                         }
288                         SLOW_TERMINAL_EXIT;
289                 }
290                 NEXT_LETTER;
291         } while(still_testing());
292         pad_test_shutdown(t, auto_right_margin == 0);
293         /* note:  tty_frame_size is the real framesize times two.
294            This takes care of half bits. */
295         rate = (tx_cps * tty_frame_size) >> 1;
296         if (rate > tty_baud_rate) {
297                 tty_baud_rate = rate;
298         }
299         if (tx_cps > tty_cps) {
300                 tty_cps = tx_cps;
301         }
302         sprintf(temp, "%lu characters per second.  Baudrate %d  ", tx_cps, j);
303         ptext(temp);
304         generic_done_message(t, state, ch);
305 }
306
307 /*
308 **      sync_lines(test_list, status, ch)
309 **
310 **      How many newlines/second?
311 */
312 static void
313 sync_lines(
314         struct test_list *t,
315         int *state,
316         int *ch)
317 {
318         int j;
319
320         if (skip_pad_test(t, state, ch,
321                 "(nel) Start scroll performance test")) {
322                 return;
323         }
324         pad_test_startup(0);
325         repeats = 100;
326         do {
327                 sprintf(temp, "%d", test_complete);
328                 put_str(temp);
329                 put_newlines(repeats);
330         } while(still_testing());
331         pad_test_shutdown(t, 0);
332         j = sliding_scale(tx_count[0], 1000000, usec_run_time);
333         if (j > tty_newline_rate) {
334                 tty_newline_rate = j;
335         }
336         sprintf(temp, "%d linefeeds per second.  ", j);
337         ptext(temp);
338         generic_done_message(t, state, ch);
339 }
340
341 /*
342 **      sync_clear(test_list, status, ch)
343 **
344 **      How many clear-screens/second?
345 */
346 static void
347 sync_clear(
348         struct test_list *t,
349         int *state,
350         int *ch)
351 {
352         int j;
353
354         if (!clear_screen) {
355                 ptext("Terminal can not clear-screen.  ");
356                 generic_done_message(t, state, ch);
357                 return;
358         }
359         if (skip_pad_test(t, state, ch,
360                 "(clear) Start clear-screen performance test")) {
361                 return;
362         }
363         pad_test_startup(0);
364         repeats = 20;
365         do {
366                 sprintf(temp, "%d", test_complete);
367                 put_str(temp);
368                 for (j = 0; j < repeats; j++) {
369                         put_clear();
370                 }
371         } while(still_testing());
372         pad_test_shutdown(t, 0);
373         j = sliding_scale(tx_count[0], 1000000, usec_run_time);
374         if (j > tty_clear_rate) {
375                 tty_clear_rate = j;
376         }
377         sprintf(temp, "%d clear-screens per second.  ", j);
378         ptext(temp);
379         generic_done_message(t, state, ch);
380 }
381
382 /*
383 **      sync_summary(test_list, status, ch)
384 **
385 **      Print out the test results.
386 */
387 static void
388 sync_summary(
389         struct test_list *t,
390         int *state,
391         int *ch)
392 {
393         char size[32];
394
395         put_crlf();
396         ptextln("Terminal  size    characters/sec linefeeds/sec  clears/sec");
397         sprintf(size, "%dx%d", columns, lines);
398         sprintf(temp, "%-10s%-11s%11lu   %11d %11d", tty_basename, size,
399                 tty_cps, tty_newline_rate, tty_clear_rate);
400         ptextln(temp);
401         generic_done_message(t, state, ch);
402 }
403
404 /*
405 **      sync_test(menu)
406 **
407 **      Run at the beginning of the pad tests and function key tests
408 */
409 void
410 sync_test(
411         struct test_menu *menu)
412 {
413         control_init();
414         if (tty_can_sync == SYNC_NOT_TESTED) {
415                 verify_time();
416         }
417         if (menu->menu_title) {
418                 put_crlf();
419                 ptextln(menu->menu_title);
420         }
421 }
422
423 /*
424 **      sync_handshake(test_list, status, ch)
425 **
426 **      Test or retest the ENQ/ACK handshake
427 */
428 void
429 sync_handshake(
430         struct test_list *t GCC_UNUSED,
431         int *state GCC_UNUSED,
432         int *ch GCC_UNUSED)
433 {
434         tty_can_sync = SYNC_NOT_TESTED;
435         verify_time();
436 }