ncurses 4.1
[ncurses.git] / ncurses / lib_mvcur.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                ncurses is copyright (C) 1992-1995                        *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *        Permission is hereby granted to reproduce and distribute ncurses  *
12 *        by any means and for any fee, whether alone or as part of a       *
13 *        larger distribution, in source or in binary form, PROVIDED        *
14 *        this notice is included with any such distribution, and is not    *
15 *        removed from any of its header files. Mention of ncurses in any   *
16 *        applications linked with it is highly appreciated.                *
17 *                                                                          *
18 *        ncurses comes AS IS with no warranty, implied or expressed.       *
19 *                                                                          *
20 ***************************************************************************/
21
22
23 /*
24 **      lib_mvcur.c
25 **
26 **      The routines for moving the physical cursor and scrolling:
27 **
28 **              void _nc_mvcur_init(void), mvcur_wrap(void)
29 **
30 **              void _nc_mvcur_resume(void)
31 **
32 **              int mvcur(int old_y, int old_x, int new_y, int new_x)
33 **
34 **              void _nc_mvcur_wrap(void)
35 **
36 **              int _nc_mvcur_scrolln(int n, int top, int bot, int maxy)
37 **
38 ** Comparisons with older movement optimizers:
39 **    SVr3 curses mvcur() can't use cursor_to_ll or auto_left_margin.
40 **    4.4BSD curses can't use cuu/cud/cuf/cub/hpa/vpa/tab/cbt for local
41 ** motions.  It doesn't use tactics based on auto_left_margin.  Weirdly
42 ** enough, it doesn't use its own hardware-scrolling routine to scroll up
43 ** destination lines for out-of-bounds addresses!
44 **    old ncurses optimizer: less accurate cost computations (in fact,
45 ** it was broken and had to be commented out!).
46 **
47 ** Compile with -DMAIN to build an interactive tester/timer for the movement
48 ** optimizer.  You can use it to investigate the optimizer's behavior.
49 ** You can also use it for tuning the formulas used to determine whether
50 ** or not full optimization is attempted.
51 **
52 ** This code has a nasty tendency to find bugs in terminfo entries, because it
53 ** exercises the non-cup movement capabilities heavily.  If you think you've
54 ** found a bug, try deleting subsets of the following capabilities (arranged
55 ** in decreasing order of suspiciousness): it, tab, cbt, hpa, vpa, cuu, cud,
56 ** cuf, cub, cuu1, cud1, cuf1, cub1.  It may be that one or more are wrong.
57 **
58 ** Note: you should expect this code to look like a resource hog in a profile.
59 ** That's because it does a lot of I/O, through the tputs() calls.  The I/O
60 ** cost swamps the computation overhead (and as machines get faster, this
61 ** will become even more true).  Comments in the test exerciser at the end
62 ** go into detail about tuning and how you can gauge the optimizer's
63 ** effectiveness.
64 **/
65
66 /****************************************************************************
67  *
68  * Constants and macros for optimizer tuning.
69  *
70  ****************************************************************************/
71
72 /*
73  * The average overhead of a full optimization computation in character
74  * transmission times.  If it's too high, the algorithm will be a bit
75  * over-biased toward using cup rather than local motions; if it's too
76  * low, the algorithm may spend more time than is strictly optimal
77  * looking for non-cup motions.  Profile the optimizer using the `t'
78  * command of the exerciser (see below), and round to the nearest integer.
79  *
80  * Yes, I (esr) thought about computing expected overhead dynamically, say
81  * by derivation from a running average of optimizer times.  But the
82  * whole point of this optimization is to *decrease* the frequency of
83  * system calls. :-)
84  */
85 #define COMPUTE_OVERHEAD        1       /* I use a 90MHz Pentium @ 9.6Kbps */
86
87 /*
88  * LONG_DIST is the distance we consider to be just as costly to move over as a
89  * cup sequence is to emit.  In other words, it's the length of a cup sequence
90  * adjusted for average computation overhead.  The magic number is the length
91  * of "\033[yy;xxH", the typical cup sequence these days.
92  */
93 #define LONG_DIST               (8 - COMPUTE_OVERHEAD)
94
95 /*
96  * Tell whether a motion is optimizable by local motions.  Needs to be cheap to
97  * compute. In general, all the fast moves go to either the right or left edge
98  * of the screen.  So any motion to a location that is (a) further away than
99  * LONG_DIST and (b) further inward from the right or left edge than LONG_DIST,
100  * we'll consider nonlocal.
101  */
102 #define NOT_LOCAL(fy, fx, ty, tx)       ((tx > LONG_DIST) && (tx < screen_lines - 1 - LONG_DIST) && (abs(ty-fy) + abs(tx-fx) > LONG_DIST))
103
104 /****************************************************************************
105  *
106  * External interfaces
107  *
108  ****************************************************************************/
109
110 /*
111  * For this code to work OK, the following components must live in the
112  * screen structure:
113  *
114  *      int             _char_padding;  // cost of character put
115  *      int             _cr_cost;       // cost of (carriage_return)
116  *      int             _cup_cost;      // cost of (cursor_address)
117  *      int             _home_cost;     // cost of (cursor_home)
118  *      int             _ll_cost;       // cost of (cursor_to_ll)
119  *#ifdef TABS_OK
120  *      int             _ht_cost;       // cost of (tab)
121  *      int             _cbt_cost;      // cost of (backtab)
122  *#endif TABS_OK
123  *      int             _cub1_cost;     // cost of (cursor_left)
124  *      int             _cuf1_cost;     // cost of (cursor_right)
125  *      int             _cud1_cost;     // cost of (cursor_down)
126  *      int             _cuu1_cost;     // cost of (cursor_up)
127  *      int             _cub_cost;      // cost of (parm_cursor_left)
128  *      int             _cuf_cost;      // cost of (parm_cursor_right)
129  *      int             _cud_cost;      // cost of (parm_cursor_down)
130  *      int             _cuu_cost;      // cost of (parm_cursor_up)
131  *      int             _hpa_cost;      // cost of (column_address)
132  *      int             _vpa_cost;      // cost of (row_address)
133  *      int             _ech_cost;      // cost of (erase_chars)
134  *      int             _rep_cost;      // cost of (repeat_char)
135  *
136  * The TABS_OK switch controls whether it is reliable to use tab/backtabs
137  * for local motions.  On many systems, it's not, due to uncertainties about
138  * tab delays and whether or not tabs will be expanded in raw mode.  If you
139  * have parm_right_cursor, tab motions don't win you a lot anyhow.
140  */
141
142 #include <curses.priv.h>
143 #include <term.h>
144 #include <ctype.h>
145
146 MODULE_ID("$Id: lib_mvcur.c,v 1.37 1997/05/03 22:15:26 Peter.Wemm Exp $")
147
148 #define STRLEN(s)       (s != 0) ? strlen(s) : 0
149
150 #define CURRENT_ATTR    SP->_current_attr       /* current phys attribute */
151 #define CURRENT_ROW     SP->_cursrow            /* phys cursor row */
152 #define CURRENT_COLUMN  SP->_curscol            /* phys cursor column */
153 #define REAL_ATTR       SP->_current_attr       /* phys current attribute */
154 #define WANT_CHAR(y, x) SP->_newscr->_line[y].text[x]   /* desired state */
155 #define BAUDRATE        SP->_baudrate           /* bits per second */
156
157 #ifdef MAIN
158 #include <sys/time.h>
159
160 static bool profiling = FALSE;
161 static float diff;
162 #endif /* MAIN */
163
164 #define OPT_SIZE 512
165
166 static void save_curs(void);
167 static void restore_curs(void);
168 static int cost_of(const char *const cap, int affcnt);
169 static int normalized_cost(const char *const cap, int affcnt);
170
171 /****************************************************************************
172  *
173  * Initialization/wrapup (including cost pre-computation)
174  *
175  ****************************************************************************/
176
177 #ifdef TRACE
178 static int
179 trace_cost_of(const char *capname, const char *cap, int affcnt)
180 {
181         int result = cost_of(cap,affcnt);
182         TR(TRACE_CHARPUT|TRACE_MOVE, ("CostOf %s %d", capname, result));
183         return result;
184 }
185 #define CostOf(cap,affcnt) trace_cost_of(#cap,cap,affcnt);
186
187 static int
188 trace_normalized_cost(const char *capname, const char *cap, int affcnt)
189 {
190         int result = normalized_cost(cap,affcnt);
191         TR(TRACE_CHARPUT|TRACE_MOVE, ("NormalizedCost %s %d", capname, result));
192         return result;
193 }
194 #define NormalizedCost(cap,affcnt) trace_normalized_cost(#cap,cap,affcnt);
195
196 #else
197
198 #define CostOf(cap,affcnt) cost_of(cap,affcnt);
199 #define NormalizedCost(cap,affcnt) normalized_cost(cap,affcnt);
200
201 #endif
202
203 static int cost_of(const char *const cap, int affcnt)
204 /* compute the cost of a given operation */
205 {
206     if (cap == 0)
207         return(INFINITY);
208     else
209     {
210         const   char    *cp;
211         float   cum_cost = 0;
212
213         for (cp = cap; *cp; cp++)
214         {
215             /* extract padding, either mandatory or required */
216             if (cp[0] == '$' && cp[1] == '<' && strchr(cp, '>'))
217             {
218                 float   number = 0;
219
220                 for (cp += 2; *cp != '>'; cp++)
221                 {
222                     if (isdigit(*cp))
223                         number = number * 10 + (*cp - '0');
224                     else if (*cp == '.')
225                         number += (*++cp - 10) / 10.0;
226                     else if (*cp == '*')
227                         number *= affcnt;
228                 }
229
230                 cum_cost += number * 10;
231             }
232             else
233                 cum_cost += SP->_char_padding;
234         }
235
236         return((int)cum_cost);
237     }
238 }
239
240 static int normalized_cost(const char *const cap, int affcnt)
241 /* compute the effective character-count for an operation (round up) */
242 {
243         int cost = cost_of(cap, affcnt);
244         if (cost != INFINITY)
245                 cost = (cost + SP->_char_padding - 1) / SP->_char_padding;
246         return cost;
247 }
248
249 static void reset_scroll_region(void)
250 /* Set the scroll-region to a known state (the default) */
251 {
252     if (change_scroll_region)
253     {
254         /* change_scroll_region may trash the cursor location */
255         save_curs();
256         TPUTS_TRACE("change_scroll_region");
257         putp(tparm(change_scroll_region, 0, screen_lines - 1));
258         restore_curs();
259     }
260 }
261
262 void _nc_mvcur_resume(void)
263 /* what to do at initialization time and after each shellout */
264 {
265     /* initialize screen for cursor access */
266     if (enter_ca_mode)
267     {
268         TPUTS_TRACE("enter_ca_mode");
269         putp(enter_ca_mode);
270     }
271
272     /*
273      * Doing this here rather than in _nc_mvcur_wrap() ensures that
274      * ncurses programs will see a reset scroll region even if a
275      * program that messed with it died ungracefully.
276      *
277      * This also undoes the effects of terminal init strings that assume
278      * they know the screen size.  This is useful when you're running
279      * a vt100 emulation through xterm.
280      */
281     reset_scroll_region();
282 }
283
284 void _nc_mvcur_init(void)
285 /* initialize the cost structure */
286 {
287     /*
288      * 9 = 7 bits + 1 parity + 1 stop.
289      */
290     SP->_char_padding = (9 * 1000 * 10) / (BAUDRATE > 0 ? BAUDRATE : 9600);
291     if (SP->_char_padding <= 0)
292         SP->_char_padding = 1;  /* must be nonzero */
293     TR(TRACE_CHARPUT|TRACE_MOVE, ("char_padding %d msecs", SP->_char_padding));
294
295     /* non-parameterized local-motion strings */
296     SP->_cr_cost   = CostOf(carriage_return, 0);
297     SP->_home_cost = CostOf(cursor_home, 0);
298     SP->_ll_cost   = CostOf(cursor_to_ll, 0);
299 #ifdef TABS_OK
300     SP->_ht_cost   = CostOf(tab, 0);
301     SP->_cbt_cost  = CostOf(back_tab, 0);
302 #endif /* TABS_OK */
303     SP->_cub1_cost = CostOf(cursor_left, 0);
304     SP->_cuf1_cost = CostOf(cursor_right, 0);
305     SP->_cud1_cost = CostOf(cursor_down, 0);
306     SP->_cuu1_cost = CostOf(cursor_up, 0);
307
308     /*
309      * Assumption: if the terminal has memory_relative addressing, the
310      * initialization strings or smcup will set single-page mode so we
311      * can treat it like absolute screen addressing.  This seems to be true
312      * for all cursor_mem_address terminal types in the terminfo database.
313      */
314     SP->_address_cursor = cursor_address ? cursor_address : cursor_mem_address;
315
316     /*
317      * Parametrized local-motion strings.  This static cost computation
318      * depends on the following assumptions:
319      *
320      * (1) They never have * padding.  In the entire master terminfo database
321      *     as of March 1995, only the obsolete Zenith Z-100 pc violates this.
322      *     (Proportional padding is found mainly in insert, delete and scroll
323      *     capabilities).
324      *
325      * (2) The average case of cup has two two-digit parameters.  Strictly,
326      *     the average case for a 24 * 80 screen has ((10*10*(1 + 1)) +
327      *     (14*10*(1 + 2)) + (10*70*(2 + 1)) + (14*70*4)) / (24*80) = 3.458
328      *     digits of parameters.  On a 25x80 screen the average is 3.6197.
329      *     On larger screens the value gets much closer to 4.
330      *
331      * (3) The average case of cub/cuf/hpa/ech/rep has 2 digits of parameters
332      *     (strictly, (((10 * 1) + (70 * 2)) / 80) = 1.8750).
333      *
334      * (4) The average case of cud/cuu/vpa has 2 digits of parameters
335      *     (strictly, (((10 * 1) + (14 * 2)) / 24) = 1.5833).
336      *
337      * All these averages depend on the assumption that all parameter values
338      * are equally probable.
339      */
340     SP->_cup_cost  = CostOf(tparm(SP->_address_cursor, 23, 23), 1);
341     SP->_cub_cost  = CostOf(tparm(parm_left_cursor, 23), 1);
342     SP->_cuf_cost  = CostOf(tparm(parm_right_cursor, 23), 1);
343     SP->_cud_cost  = CostOf(tparm(parm_down_cursor, 23), 1);
344     SP->_cuu_cost  = CostOf(tparm(parm_up_cursor, 23), 1);
345     SP->_hpa_cost  = CostOf(tparm(column_address, 23), 1);
346     SP->_vpa_cost  = CostOf(tparm(row_address, 23), 1);
347
348     /* non-parameterized screen-update strings */
349     SP->_ed_cost   = NormalizedCost(clr_eos, 1);
350     SP->_el_cost   = NormalizedCost(clr_eol, 1);
351     SP->_el1_cost  = NormalizedCost(clr_bol, 1);
352     SP->_dch1_cost = NormalizedCost(delete_character, 1);
353     SP->_ich1_cost = NormalizedCost(insert_character, 1);
354
355     /* parameterized screen-update strings */
356     SP->_dch_cost  = NormalizedCost(tparm(parm_dch, 23), 1);
357     SP->_ich_cost  = NormalizedCost(tparm(parm_ich, 23), 1);
358     SP->_ech_cost  = NormalizedCost(tparm(erase_chars, 23), 1);
359     SP->_rep_cost  = NormalizedCost(tparm(repeat_char, ' ', 23), 1);
360
361     SP->_cup_ch_cost = NormalizedCost(tparm(SP->_address_cursor, 23, 23), 1);
362     SP->_hpa_ch_cost = NormalizedCost(tparm(column_address, 23), 1);
363
364     /* pre-compute some capability lengths */
365     SP->_carriage_return_length = STRLEN(carriage_return);
366     SP->_cursor_home_length     = STRLEN(cursor_home);
367     SP->_cursor_to_ll_length    = STRLEN(cursor_to_ll);
368
369     /*
370      * A different, possibly better way to arrange this would be to set
371      * SP->_endwin = TRUE at window initialization time and let this be
372      * called by doupdate's return-from-shellout code.
373      */
374     _nc_mvcur_resume();
375 }
376
377 void _nc_mvcur_wrap(void)
378 /* wrap up cursor-addressing mode */
379 {
380     reset_scroll_region();
381     if (exit_ca_mode)
382     {
383         TPUTS_TRACE("exit_ca_mode");
384         putp(exit_ca_mode);
385     }
386 }
387
388 /****************************************************************************
389  *
390  * Optimized cursor movement
391  *
392  ****************************************************************************/
393
394 /*
395  * Perform repeated-append, returning cost
396  */
397 static inline int
398 repeated_append (int total, int num, int repeat, char *dst, const char *src)
399 {
400         register size_t src_len = strlen(src);
401         register size_t dst_len = STRLEN(dst);
402
403         if ((dst_len + repeat * src_len) < OPT_SIZE-1) {
404                 total += (num * repeat);
405                 if (dst) {
406                     dst += dst_len;
407                     while (repeat-- > 0) {
408                         (void) strcpy(dst, src);
409                         dst += src_len;
410                     }
411                 }
412         } else {
413                 total = INFINITY;
414         }
415         return total;
416 }
417
418 #ifndef NO_OPTIMIZE
419 #define NEXTTAB(fr)     (fr + init_tabs - (fr % init_tabs))
420 #define LASTTAB(fr)     (fr - init_tabs + (fr % init_tabs))
421
422 /* Note: we'd like to inline this for speed, but GNU C barfs on the attempt. */
423
424 static int
425 relative_move(char *result, int from_y,int from_x,int to_y,int to_x, bool ovw)
426 /* move via local motions (cuu/cuu1/cud/cud1/cub1/cub/cuf1/cuf/vpa/hpa) */
427 {
428     int         n, vcost = 0, hcost = 0;
429
430     if (result)
431         result[0] = '\0';
432
433     if (to_y != from_y)
434     {
435         vcost = INFINITY;
436
437         if (row_address)
438         {
439             if (result)
440                 (void) strcpy(result, tparm(row_address, to_y));
441             vcost = SP->_vpa_cost;
442         }
443
444         if (to_y > from_y)
445         {
446             n = (to_y - from_y);
447
448             if (parm_down_cursor && SP->_cud_cost < vcost)
449             {
450                 if (result)
451                     (void) strcpy(result, tparm(parm_down_cursor, n));
452                 vcost = SP->_cud_cost;
453             }
454
455             if (cursor_down && (n * SP->_cud1_cost < vcost))
456             {
457                 if (result)
458                     result[0] = '\0';
459                 vcost = repeated_append(vcost, SP->_cud1_cost, n, result, cursor_down);
460             }
461         }
462         else /* (to_y < from_y) */
463         {
464             n = (from_y - to_y);
465
466             if (parm_up_cursor && SP->_cup_cost < vcost)
467             {
468                 if (result)
469                     (void) strcpy(result, tparm(parm_up_cursor, n));
470                 vcost = SP->_cup_cost;
471             }
472
473             if (cursor_up && (n * SP->_cuu1_cost < vcost))
474             {
475                 if (result)
476                     result[0] = '\0';
477                 vcost = repeated_append(vcost, SP->_cuu1_cost, n, result, cursor_up);
478             }
479         }
480
481         if (vcost == INFINITY)
482             return(INFINITY);
483     }
484
485     if (result)
486         result += strlen(result);
487
488     if (to_x != from_x)
489     {
490         char    try[OPT_SIZE];
491
492         hcost = INFINITY;
493
494         if (column_address)
495         {
496             if (result)
497                 (void) strcpy(result, tparm(column_address, to_x));
498             hcost = SP->_hpa_cost;
499         }
500
501         if (to_x > from_x)
502         {
503             n = to_x - from_x;
504
505             if (parm_right_cursor && SP->_cuf_cost < hcost)
506             {
507                 if (result)
508                     (void) strcpy(result, tparm(parm_right_cursor, n));
509                 hcost = SP->_cuf_cost;
510             }
511
512             if (cursor_right)
513             {
514                 int     lhcost = 0;
515
516                 try[0] = '\0';
517
518 #ifdef TABS_OK
519                 /* use hard tabs, if we have them, to do as much as possible */
520                 if (init_tabs > 0 && tab)
521                 {
522                     int nxt, fr;
523
524                     for (fr = from_x; (nxt = NEXTTAB(fr)) <= to_x; fr = nxt)
525                     {
526                         lhcost = repeated_append(lhcost, SP->_ht_cost, 1, try, tab);
527                         if (lhcost == INFINITY)
528                                 break;
529                     }
530
531                     n = to_x - fr;
532                     from_x = fr;
533                 }
534 #endif /* TABS_OK */
535
536 #if defined(REAL_ATTR) && defined(WANT_CHAR)
537                 /*
538                  * If we have no attribute changes, overwrite is cheaper.
539                  * Note: must suppress this by passing in ovw = FALSE whenever
540                  * WANT_CHAR would return invalid data.  In particular, this
541                  * is true between the time a hardware scroll has been done
542                  * and the time the structure WANT_CHAR would access has been
543                  * updated.
544                  */
545                 if (ovw)
546                 {
547                     int i;
548
549                     for (i = 0; i < n; i++)
550                         if ((WANT_CHAR(to_y, from_x + i) & A_ATTRIBUTES) != CURRENT_ATTR)
551                         {
552                             ovw = FALSE;
553                             break;
554                         }
555                 }
556                 if (ovw)
557                 {
558                     char        *sp;
559                     int i;
560
561                     sp = try + strlen(try);
562
563                     for (i = 0; i < n; i++)
564                         *sp++ = WANT_CHAR(to_y, from_x + i);
565                     *sp = '\0';
566                     lhcost += n * SP->_char_padding;
567                 }
568                 else
569 #endif /* defined(REAL_ATTR) && defined(WANT_CHAR) */
570                 {
571                     lhcost = repeated_append(lhcost, SP->_cuf1_cost, n, try, cursor_right);
572                 }
573
574                 if (lhcost < hcost)
575                 {
576                     if (result)
577                         (void) strcpy(result, try);
578                     hcost = lhcost;
579                 }
580             }
581         }
582         else /* (to_x < from_x) */
583         {
584             n = from_x - to_x;
585
586             if (parm_left_cursor && SP->_cub_cost < hcost)
587             {
588                 if (result)
589                     (void) strcpy(result, tparm(parm_left_cursor, n));
590                 hcost = SP->_cub_cost;
591             }
592
593             if (cursor_left)
594             {
595                 int     lhcost = 0;
596
597                 try[0] = '\0';
598
599 #ifdef TABS_OK
600                 if (init_tabs > 0 && back_tab)
601                 {
602                     int nxt, fr;
603
604                     for (fr = from_x; (nxt = LASTTAB(fr)) >= to_x; fr = nxt)
605                     {
606                         lhcost = repeated_append(lhcost, SP->_cbt_cost, 1, try, back_tab);
607                         if (lhcost == INFINITY)
608                                 break;
609                     }
610
611                     n = to_x - fr;
612                 }
613 #endif /* TABS_OK */
614
615                 lhcost = repeated_append(lhcost, SP->_cub1_cost, n, try, cursor_left);
616
617                 if (lhcost < hcost)
618                 {
619                     if (result)
620                         (void) strcpy(result, try);
621                     hcost = lhcost;
622                 }
623             }
624         }
625
626         if (hcost == INFINITY)
627             return(INFINITY);
628     }
629
630     return(vcost + hcost);
631 }
632 #endif /* !NO_OPTIMIZE */
633
634 /*
635  * With the machinery set up above, it's conceivable that
636  * onscreen_mvcur could be modified into a recursive function that does
637  * an alpha-beta search of motion space, as though it were a chess
638  * move tree, with the weight function being boolean and the search
639  * depth equated to length of string.  However, this would jack up the
640  * computation cost a lot, especially on terminals without a cup
641  * capability constraining the search tree depth.  So we settle for
642  * the simpler method below.
643  */
644
645 static inline int
646 onscreen_mvcur(int yold,int xold,int ynew,int xnew, bool ovw)
647 /* onscreen move from (yold, xold) to (ynew, xnew) */
648 {
649     char        use[OPT_SIZE], *sp;
650     int         tactic = 0, newcost, usecost = INFINITY;
651
652 #ifdef MAIN
653     struct timeval before, after;
654
655     gettimeofday(&before, NULL);
656 #endif /* MAIN */
657
658     /* tactic #0: use direct cursor addressing */
659     sp = tparm(SP->_address_cursor, ynew, xnew);
660     if (sp)
661     {
662         tactic = 0;
663         (void) strcpy(use, sp);
664         usecost = SP->_cup_cost;
665
666 #if defined(TRACE) || defined(NCURSES_TEST)
667         if (!(_nc_optimize_enable & OPTIMIZE_MVCUR))
668             goto nonlocal;
669 #endif /* TRACE */
670
671         /*
672          * We may be able to tell in advance that the full optimization
673          * will probably not be worth its overhead.  Also, don't try to
674          * use local movement if the current attribute is anything but
675          * A_NORMAL...there are just too many ways this can screw up
676          * (like, say, local-movement \n getting mapped to some obscure
677          * character because A_ALTCHARSET is on).
678          */
679         if (yold == -1 || xold == -1  ||
680             REAL_ATTR != A_NORMAL || NOT_LOCAL(yold, xold, ynew, xnew))
681         {
682 #ifdef MAIN
683             if (!profiling)
684             {
685                 (void) fputs("nonlocal\n", stderr);
686                 goto nonlocal;  /* always run the optimizer if profiling */
687             }
688 #else
689             goto nonlocal;
690 #endif /* MAIN */
691         }
692     }
693
694 #ifndef NO_OPTIMIZE
695     /* tactic #1: use local movement */
696     if (yold != -1 && xold != -1
697                 && ((newcost=relative_move(NULL, yold, xold, ynew, xnew, ovw))!=INFINITY)
698                 && newcost < usecost)
699     {
700         tactic = 1;
701         usecost = newcost;
702     }
703
704     /* tactic #2: use carriage-return + local movement */
705     if (yold < screen_lines - 1 && xold < screen_columns - 1)
706     {
707         if (carriage_return
708                 && ((newcost=relative_move(NULL, yold,0,ynew,xnew, ovw)) != INFINITY)
709                 && SP->_cr_cost + newcost < usecost)
710         {
711             tactic = 2;
712             usecost = SP->_cr_cost + newcost;
713         }
714     }
715
716     /* tactic #3: use home-cursor + local movement */
717     if (cursor_home
718         && ((newcost=relative_move(NULL, 0, 0, ynew, xnew, ovw)) != INFINITY)
719         && SP->_home_cost + newcost < usecost)
720     {
721         tactic = 3;
722         usecost = SP->_home_cost + newcost;
723     }
724
725     /* tactic #4: use home-down + local movement */
726     if (cursor_to_ll
727         && ((newcost=relative_move(NULL, screen_lines-1, 0, ynew, xnew, ovw)) != INFINITY)
728         && SP->_ll_cost + newcost < usecost)
729     {
730         tactic = 4;
731         usecost = SP->_ll_cost + newcost;
732     }
733
734     /*
735      * tactic #5: use left margin for wrap to right-hand side,
736      * unless strange wrap behavior indicated by xenl might hose us.
737      */
738     if (auto_left_margin && !eat_newline_glitch
739         && yold > 0 && yold < screen_lines - 1 && cursor_left
740         && ((newcost=relative_move(NULL, yold-1, screen_columns-1, ynew, xnew, ovw)) != INFINITY)
741         && SP->_cr_cost + SP->_cub1_cost + newcost + newcost < usecost)
742     {
743         tactic = 5;
744         usecost = SP->_cr_cost + SP->_cub1_cost + newcost;
745     }
746
747     /*
748      * These cases are ordered by estimated relative frequency.
749      */
750     if (tactic)
751     {
752         if (tactic == 1)
753             (void) relative_move(use, yold, xold, ynew, xnew, ovw);
754         else if (tactic == 2)
755         {
756             (void) strcpy(use, carriage_return);
757             (void) relative_move(use + SP->_carriage_return_length,
758                                  yold,0,ynew,xnew, ovw);
759         }
760         else if (tactic == 3)
761         {
762             (void) strcpy(use, cursor_home);
763             (void) relative_move(use + SP->_cursor_home_length,
764                                  0, 0, ynew, xnew, ovw);
765         }
766         else if (tactic == 4)
767         {
768             (void) strcpy(use, cursor_to_ll);
769             (void) relative_move(use + SP->_cursor_to_ll_length,
770                                  screen_lines-1, 0, ynew, xnew, ovw);
771         }
772         else /* if (tactic == 5) */
773         {
774             use[0] = '\0';
775             if (xold > 0)
776                 (void) strcat(use, carriage_return);
777             (void) strcat(use, cursor_left);
778             (void) relative_move(use + strlen(use),
779                                  yold-1, screen_columns-1, ynew, xnew, ovw);
780         }
781     }
782 #endif /* !NO_OPTIMIZE */
783
784 #ifdef MAIN
785     gettimeofday(&after, NULL);
786     diff = after.tv_usec - before.tv_usec
787         + (after.tv_sec - before.tv_sec) * 1000000;
788     if (!profiling)
789         (void) fprintf(stderr, "onscreen: %d msec, %f 28.8Kbps char-equivalents\n",
790                        (int)diff, diff/288);
791 #endif /* MAIN */
792
793  nonlocal:
794     if (usecost != INFINITY)
795     {
796         TPUTS_TRACE("mvcur");
797         tputs(use, 1, _nc_outch);
798         return(OK);
799     }
800     else
801         return(ERR);
802 }
803
804 int mvcur(int yold, int xold, int ynew, int xnew)
805 /* optimized cursor move from (yold, xold) to (ynew, xnew) */
806 {
807     TR(TRACE_MOVE, ("mvcur(%d,%d,%d,%d) called", yold, xold, ynew, xnew));
808
809     if (yold == ynew && xold == xnew)
810         return(OK);
811
812     /*
813      * Most work here is rounding for terminal boundaries getting the
814      * column position implied by wraparound or the lack thereof and
815      * rolling up the screen to get ynew on the screen.
816      */
817
818     if (xnew >= screen_columns)
819     {
820         ynew += xnew / screen_columns;
821         xnew %= screen_columns;
822     }
823     if (xold >= screen_columns)
824     {
825         int     l;
826
827         l = (xold + 1) / screen_columns;
828         yold += l;
829         if (yold >= screen_lines)
830                 l -= (yold - screen_lines - 1);
831
832         while (l > 0) {
833                 if (newline)
834                 {
835                         TPUTS_TRACE("newline");
836                         tputs(newline, 0, _nc_outch);
837                 }
838                 else
839                         putchar('\n');
840                 l--;
841                 if (xold > 0)
842                 {
843                         if (carriage_return)
844                         {
845                                 TPUTS_TRACE("carriage_return");
846                                 tputs(carriage_return, 0, _nc_outch);
847                         }
848                         else
849                                 putchar('\r');
850                         xold = 0;
851                 }
852         }
853     }
854
855     if (yold > screen_lines - 1)
856         yold = screen_lines - 1;
857     if (ynew > screen_lines - 1)
858         ynew = screen_lines - 1;
859
860     /* destination location is on screen now */
861     return(onscreen_mvcur(yold, xold, ynew, xnew, TRUE));
862 }
863
864
865 /****************************************************************************
866  *
867  * Cursor save_restore
868  *
869  ****************************************************************************/
870
871 /* assumption: sc/rc is faster than cursor addressing */
872
873 static int      oy, ox;         /* ugh, mvcur_scrolln() needs to see this */
874
875 static void save_curs(void)
876 {
877     if (save_cursor && restore_cursor)
878     {
879         TPUTS_TRACE("save_cursor");
880         putp(save_cursor);
881     }
882
883     oy = CURRENT_ROW;
884     ox = CURRENT_COLUMN;
885 }
886
887 static void restore_curs(void)
888 {
889     if (save_cursor && restore_cursor)
890     {
891         TPUTS_TRACE("restore_cursor");
892         putp(restore_cursor);
893     }
894     else
895         onscreen_mvcur(-1, -1, oy, ox, FALSE);
896 }
897
898 /****************************************************************************
899  *
900  * Physical-scrolling support
901  *
902  ****************************************************************************/
903
904 static int DoTheScrolling(int n, int top, int bot, int maxy)
905 /* scroll region from top to bot by n lines */
906 {
907     int i;
908
909     /*
910      * This code was adapted from Keith Bostic's hardware scrolling
911      * support for 4.4BSD curses.  I (esr) translated it to use terminfo
912      * capabilities, narrowed the call interface slightly, and cleaned
913      * up some convoluted tests.  I also added support for the memory_above
914      * memory_below, and non_dest_scroll_region capabilities.
915      *
916      * For this code to work, we must have either
917      * change_scroll_region and scroll forward/reverse commands, or
918      * insert and delete line capabilities.
919      * When the scrolling region has been set, the cursor has to
920      * be at the last line of the region to make the scroll
921      * happen.
922      *
923      * This code makes one aesthetic decision in the opposite way from
924      * BSD curses.  BSD curses preferred pairs of il/dl operations
925      * over scrolls, allegedly because il/dl looked faster.  We, on
926      * the other hand, prefer scrolls because (a) they're just as fast
927      * on many terminals and (b) using them avoids bouncing an
928      * unchanged bottom section of the screen up and down, which is
929      * visually nasty.
930      */
931     if (n > 0)
932     {
933         /*
934          * Explicitly clear if stuff pushed off top of region might
935          * be saved by the terminal.
936          */
937         if (non_dest_scroll_region || (memory_above && top == 0)) {
938             for (i = 0; i < n; i++)
939             {
940                 mvcur(-1, -1, i, 0);
941                 TPUTS_TRACE("clr_eol");
942                 tputs(clr_eol, n, _nc_outch);
943             }
944         }
945
946         if (change_scroll_region && (scroll_forward || parm_index))
947         {
948             TPUTS_TRACE("change_scroll_region");
949             tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
950
951             onscreen_mvcur(-1, -1, bot, 0, TRUE);
952
953             if (parm_index != NULL)
954             {
955                 TPUTS_TRACE("parm_index");
956                 tputs(tparm(parm_index, n, 0), n, _nc_outch);
957             }
958             else
959             {
960                 for (i = 0; i < n; i++)
961                 {
962                     TPUTS_TRACE("scroll_forward");
963                     tputs(scroll_forward, 0, _nc_outch);
964                 }
965             }
966             TPUTS_TRACE("change_scroll_region");
967             tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
968         }
969         else if (parm_index && top == 0 && bot == maxy)
970         {
971             onscreen_mvcur(oy, ox, bot, 0, TRUE);
972             TPUTS_TRACE("parm_index");
973             tputs(tparm(parm_index, n, 0), n, _nc_outch);
974         }
975         else if (scroll_forward && top == 0 && bot == maxy)
976         {
977             onscreen_mvcur(oy, ox, bot, 0, TRUE);
978             for (i = 0; i < n; i++)
979             {
980                 TPUTS_TRACE("scroll_forward");
981                 tputs(scroll_forward, 0, _nc_outch);
982             }
983         }
984         else if (_nc_idlok
985          && (parm_delete_line || delete_line)
986          && (parm_insert_line || insert_line))
987         {
988             onscreen_mvcur(oy, ox, top, 0, TRUE);
989
990             if (parm_delete_line)
991             {
992                 TPUTS_TRACE("parm_delete_line");
993                 tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
994             }
995             else
996             {
997                 for (i = 0; i < n; i++)
998                 {
999                     TPUTS_TRACE("parm_index");
1000                     tputs(delete_line, 0, _nc_outch);
1001                 }
1002             }
1003
1004             onscreen_mvcur(top, 0, bot - n + 1, 0, FALSE);
1005
1006             /* Push down the bottom region. */
1007             if (parm_insert_line)
1008             {
1009                 TPUTS_TRACE("parm_insert_line");
1010                 tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
1011             }
1012             else
1013             {
1014                 for (i = 0; i < n; i++)
1015                 {
1016                     TPUTS_TRACE("insert_line");
1017                     tputs(insert_line, 0, _nc_outch);
1018                 }
1019             }
1020         }
1021         else
1022             return(ERR);
1023     }
1024     else /* (n < 0) */
1025     {
1026         /*
1027          * Do explicit clear to end of region if it's possible that the
1028          * terminal might hold on to stuff we push off the end.
1029          */
1030         if (non_dest_scroll_region || (memory_below && bot == maxy))
1031         {
1032             if (bot == maxy && clr_eos)
1033             {
1034                 mvcur(-1, -1, lines + n, 0);
1035                 TPUTS_TRACE("clr_eos");
1036                 tputs(clr_eos, n, _nc_outch);
1037             }
1038             else if (clr_eol)
1039             {
1040                 for (i = 0; i < -n; i++)
1041                 {
1042                     mvcur(-1, -1, lines + n + i, 0);
1043                     TPUTS_TRACE("clr_eol");
1044                     tputs(clr_eol, n, _nc_outch);
1045                 }
1046             }
1047         }
1048
1049         if (change_scroll_region && (scroll_reverse || parm_rindex))
1050         {
1051             TPUTS_TRACE("change_scroll_region");
1052             tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
1053
1054             onscreen_mvcur(-1, -1, top, 0, TRUE);
1055
1056             if (parm_rindex)
1057             {
1058                 TPUTS_TRACE("parm_rindex");
1059                 tputs(tparm(parm_rindex, -n, 0), -n, _nc_outch);
1060             }
1061             else
1062             {
1063                 for (i = n; i < 0; i++)
1064                 {
1065                     TPUTS_TRACE("scroll_reverse");
1066                     tputs(scroll_reverse, 0, _nc_outch);
1067                 }
1068             }
1069             TPUTS_TRACE("change_scroll_region");
1070             tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
1071         }
1072         else if (parm_rindex && top == 0 && bot == maxy)
1073         {
1074             onscreen_mvcur(oy, ox, bot + n + 1, 0, TRUE);
1075
1076             TPUTS_TRACE("parm_rindex");
1077             tputs(tparm(parm_rindex, -n, 0), -n, _nc_outch);
1078         }
1079         else if (scroll_reverse && top == 0 && bot == maxy)
1080         {
1081             onscreen_mvcur(-1, -1, 0, 0, TRUE);
1082             for (i = n; i < 0; i++)
1083             {
1084                 TPUTS_TRACE("scroll_reverse");
1085                 tputs(scroll_reverse, 0, _nc_outch);
1086             }
1087         }
1088         else if (_nc_idlok
1089          && (parm_delete_line || delete_line)
1090          && (parm_insert_line || insert_line))
1091         {
1092             onscreen_mvcur(oy, ox, bot + n + 1, 0, TRUE);
1093
1094             if (parm_delete_line)
1095             {
1096                 TPUTS_TRACE("parm_delete_line");
1097                 tputs(tparm(parm_delete_line, -n, 0), -n, _nc_outch);
1098             }
1099             else
1100             {
1101                 for (i = n; i < 0; i++)
1102                 {
1103                     TPUTS_TRACE("delete_line");
1104                     tputs(delete_line, 0, _nc_outch);
1105                 }
1106             }
1107
1108             onscreen_mvcur(bot + n + 1, 0, top, 0, FALSE);
1109
1110             /* Scroll the block down. */
1111             if (parm_insert_line)
1112             {
1113                 TPUTS_TRACE("parm_insert_line");
1114                 tputs(tparm(parm_insert_line, -n, 0), -n, _nc_outch);
1115             }
1116             else
1117             {
1118                 for (i = n; i < 0; i++)
1119                 {
1120                     TPUTS_TRACE("insert_line");
1121                     tputs(insert_line, 0, _nc_outch);
1122                 }
1123             }
1124         }
1125         else
1126             return(ERR);
1127     }
1128
1129     return(OK);
1130 }
1131
1132 int _nc_mvcur_scrolln(int n, int top, int bot, int maxy)
1133 /* scroll region from top to bot by n lines */
1134 {
1135     int code;
1136
1137     TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
1138
1139     save_curs();
1140     code = DoTheScrolling(n, top, bot, maxy);
1141     restore_curs();
1142     return(code);
1143 }
1144
1145 #ifdef MAIN
1146 /****************************************************************************
1147  *
1148  * Movement optimizer test code
1149  *
1150  ****************************************************************************/
1151
1152 #include <tic.h>
1153 #include <dump_entry.h>
1154
1155 char *_nc_progname = "mvcur";
1156
1157 static unsigned long xmits;
1158
1159 int tputs(const char *string, int affcnt, int (*outc)(int))
1160 /* stub tputs() that dumps sequences in a visible form */
1161 {
1162     if (profiling)
1163         xmits += strlen(string);
1164     else
1165         (void) fputs(_nc_visbuf(string), stdout);
1166     return(OK);
1167 }
1168
1169 int putp(const char *string)
1170 {
1171     return(tputs(string, 1, _nc_outch));
1172 }
1173
1174 int _nc_outch(int ch)
1175 {
1176     putc(ch, stdout);
1177     return OK;
1178 }
1179
1180 static char     tname[BUFSIZ];
1181
1182 static void load_term(void)
1183 {
1184     (void) setupterm(tname, STDOUT_FILENO, NULL);
1185 }
1186
1187 static int roll(int n)
1188 {
1189     int i, j;
1190
1191     i = (RAND_MAX / n) * n;
1192     while ((j = rand()) >= i)
1193         continue;
1194     return (j % n);
1195 }
1196
1197 int main(int argc, char *argv[])
1198 {
1199     (void) strcpy(tname, getenv("TERM"));
1200     load_term();
1201     _nc_setupscreen(lines, columns, stdout);
1202     baudrate();
1203
1204     _nc_mvcur_init();
1205 #if HAVE_SETVBUF || HAVE_SETBUFFER
1206     /*
1207      * Undo the effects of our optimization hack, otherwise our interactive
1208      * prompts don't flush properly.
1209      */
1210 #if HAVE_SETVBUF
1211     (void) setvbuf(SP->_ofp, malloc(BUFSIZ), _IOLBF, BUFSIZ);
1212 #elif HAVE_SETBUFFER
1213     (void) setbuffer(SP->_ofp, malloc(BUFSIZ), BUFSIZ);
1214 #endif
1215 #endif /* HAVE_SETVBUF || HAVE_SETBUFFER */
1216
1217     (void) puts("The mvcur tester.  Type ? for help");
1218
1219     fputs("smcup:", stdout);
1220     putchar('\n');
1221
1222     for (;;)
1223     {
1224         int     fy, fx, ty, tx, n, i;
1225         char    buf[BUFSIZ], capname[BUFSIZ];
1226
1227         (void) fputs("> ", stdout);
1228         (void) fgets(buf, sizeof(buf), stdin);
1229
1230         if (buf[0] == '?')
1231         {
1232 (void) puts("?                -- display this help message");
1233 (void) puts("fy fx ty tx      -- (4 numbers) display (fy,fx)->(ty,tx) move");
1234 (void) puts("s[croll] n t b m -- display scrolling sequence");
1235 (void) printf("r[eload]         -- reload terminal info for %s\n",
1236               getenv("TERM"));
1237 (void) puts("l[oad] <term>    -- load terminal info for type <term>");
1238 (void) puts("d[elete] <cap>   -- delete named capability");
1239 (void) puts("i[nspect]        -- display terminal capabilities");
1240 (void) puts("c[ost]           -- dump cursor-optimization cost table");
1241 (void) puts("o[optimize]      -- toggle movement optimization");
1242 (void) puts("t[orture] <num>  -- torture-test with <num> random moves");
1243 (void) puts("q[uit]           -- quit the program");
1244         }
1245         else if (sscanf(buf, "%d %d %d %d", &fy, &fx, &ty, &tx) == 4)
1246         {
1247             struct timeval before, after;
1248
1249             putchar('"');
1250
1251             gettimeofday(&before, NULL);
1252             mvcur(fy, fx, ty, tx);
1253             gettimeofday(&after, NULL);
1254
1255             printf("\" (%ld msec)\n",
1256                 after.tv_usec - before.tv_usec + (after.tv_sec - before.tv_sec) * 1000000);
1257         }
1258         else if (sscanf(buf, "s %d %d %d %d", &fy, &fx, &ty, &tx) == 4)
1259         {
1260             struct timeval before, after;
1261
1262             putchar('"');
1263
1264             gettimeofday(&before, NULL);
1265             _nc_mvcur_scrolln(fy, fx, ty, tx);
1266             gettimeofday(&after, NULL);
1267
1268             printf("\" (%ld msec)\n",
1269                 after.tv_usec - before.tv_usec + (after.tv_sec - before.tv_sec) * 1000000);
1270         }
1271         else if (buf[0] == 'r')
1272         {
1273             (void) strcpy(tname, getenv("TERM"));
1274             load_term();
1275         }
1276         else if (sscanf(buf, "l %s", tname) == 1)
1277         {
1278             load_term();
1279         }
1280         else if (sscanf(buf, "d %s", capname) == 1)
1281         {
1282             struct name_table_entry const       *np = _nc_find_entry(capname,
1283                                                          _nc_info_hash_table);
1284
1285             if (np == NULL)
1286                 (void) printf("No such capability as \"%s\"\n", capname);
1287             else
1288             {
1289                 switch(np->nte_type)
1290                 {
1291                 case BOOLEAN:
1292                     cur_term->type.Booleans[np->nte_index] = FALSE;
1293                     (void) printf("Boolean capability `%s' (%d) turned off.\n",
1294                                   np->nte_name, np->nte_index);
1295                     break;
1296
1297                 case NUMBER:
1298                     cur_term->type.Numbers[np->nte_index] = -1;
1299                     (void) printf("Number capability `%s' (%d) set to -1.\n",
1300                                   np->nte_name, np->nte_index);
1301                     break;
1302
1303                 case STRING:
1304                     cur_term->type.Strings[np->nte_index] = (char *)NULL;
1305                     (void) printf("String capability `%s' (%d) deleted.\n",
1306                                   np->nte_name, np->nte_index);
1307                     break;
1308                 }
1309             }
1310         }
1311         else if (buf[0] == 'i')
1312         {
1313              dump_init((char *)NULL, F_TERMINFO, S_TERMINFO, 70, 0);
1314              dump_entry(&cur_term->type, NULL);
1315              putchar('\n');
1316         }
1317         else if (buf[0] == 'o')
1318         {
1319              if (_nc_optime_enable & OPTIMIZE_MVCUR)
1320              {
1321                  _nc_optimize_enable &=~ OPTIMIZE_MVCUR;
1322                  (void) puts("Optimization is now off.");
1323              }
1324              else
1325              {
1326                  _nc_optimize_enable |= OPTIMIZE_MVCUR;
1327                  (void) puts("Optimization is now on.");
1328              }
1329         }
1330         /*
1331          * You can use the `t' test to profile and tune the movement
1332          * optimizer.  Use iteration values in three digits or more.
1333          * At above 5000 iterations the profile timing averages are stable
1334          * to within a millisecond or three.
1335          *
1336          * The `overhead' field of the report will help you pick a
1337          * COMPUTE_OVERHEAD figure appropriate for your processor and
1338          * expected line speed.  The `total estimated time' is
1339          * computation time plus a character-transmission time
1340          * estimate computed from the number of transmits and the baud
1341          * rate.
1342          *
1343          * Use this together with the `o' command to get a read on the
1344          * optimizer's effectiveness.  Compare the total estimated times
1345          * for `t' runs of the same length in both optimized and un-optimized
1346          * modes.  As long as the optimized times are less, the optimizer
1347          * is winning.
1348          */
1349         else if (sscanf(buf, "t %d", &n) == 1)
1350         {
1351             float cumtime = 0, perchar;
1352             int speeds[] = {2400, 9600, 14400, 19200, 28800, 38400, 0};
1353
1354             srand((unsigned)(getpid() + time((time_t *)0)));
1355             profiling = TRUE;
1356             xmits = 0;
1357             for (i = 0; i < n; i++)
1358             {
1359                 /*
1360                  * This does a move test between two random locations,
1361                  * Random moves probably short-change the optimizer,
1362                  * which will work better on the short moves probably
1363                  * typical of doupdate()'s usage pattern.  Still,
1364                  * until we have better data...
1365                  */
1366 #ifdef FIND_COREDUMP
1367                 int from_y = roll(lines);
1368                 int to_y = roll(lines);
1369                 int from_x = roll(columns);
1370                 int to_x = roll(columns);
1371
1372                 printf("(%d,%d) -> (%d,%d)\n", from_y, from_x, to_y, to_x);
1373                 mvcur(from_y, from_x, to_y, to_x);
1374 #else
1375                 mvcur(roll(lines), roll(columns), roll(lines), roll(columns));
1376 #endif /* FIND_COREDUMP */
1377                 if (diff)
1378                     cumtime += diff;
1379             }
1380             profiling = FALSE;
1381
1382             /*
1383              * Average milliseconds per character optimization time.
1384              * This is the key figure to watch when tuning the optimizer.
1385              */
1386             perchar = cumtime / n;
1387
1388             (void) printf("%d moves (%ld chars) in %d msec, %f msec each:\n",
1389                           n, xmits, (int)cumtime, perchar);
1390
1391             for (i = 0; speeds[i]; i++)
1392             {
1393                 /*
1394                  * Total estimated time for the moves, computation and
1395                  * transmission both. Transmission time is an estimate
1396                  * assuming 9 bits/char, 8 bits + 1 stop bit.
1397                  */
1398                 float totalest = cumtime + xmits * 9 * 1e6 / speeds[i];
1399
1400                 /*
1401                  * Per-character optimization overhead in character transmits
1402                  * at the current speed.  Round this to the nearest integer
1403                  * to figure COMPUTE_OVERHEAD for the speed.
1404                  */
1405                 float overhead = speeds[i] * perchar / 1e6;
1406
1407                 (void) printf("%6d bps: %3.2f char-xmits overhead; total estimated time %15.2f\n",
1408                               speeds[i], overhead, totalest);
1409             }
1410         }
1411         else if (buf[0] == 'c')
1412         {
1413             (void) printf("char padding: %d\n", SP->_char_padding);
1414             (void) printf("cr cost: %d\n", SP->_cr_cost);
1415             (void) printf("cup cost: %d\n", SP->_cup_cost);
1416             (void) printf("home cost: %d\n", SP->_home_cost);
1417             (void) printf("ll cost: %d\n", SP->_ll_cost);
1418 #ifdef TABS_OK
1419             (void) printf("ht cost: %d\n", SP->_ht_cost);
1420             (void) printf("cbt cost: %d\n", SP->_cbt_cost);
1421 #endif /* TABS_OK */
1422             (void) printf("cub1 cost: %d\n", SP->_cub1_cost);
1423             (void) printf("cuf1 cost: %d\n", SP->_cuf1_cost);
1424             (void) printf("cud1 cost: %d\n", SP->_cud1_cost);
1425             (void) printf("cuu1 cost: %d\n", SP->_cuu1_cost);
1426             (void) printf("cub cost: %d\n", SP->_cub_cost);
1427             (void) printf("cuf cost: %d\n", SP->_cuf_cost);
1428             (void) printf("cud cost: %d\n", SP->_cud_cost);
1429             (void) printf("cuu cost: %d\n", SP->_cuu_cost);
1430             (void) printf("hpa cost: %d\n", SP->_hpa_cost);
1431             (void) printf("vpa cost: %d\n", SP->_vpa_cost);
1432         }
1433         else if (buf[0] == 'x' || buf[0] == 'q')
1434             break;
1435         else
1436             (void) puts("Invalid command.");
1437     }
1438
1439     (void) fputs("rmcup:", stdout);
1440     _nc_mvcur_wrap();
1441     putchar('\n');
1442
1443     return(0);
1444 }
1445
1446 #endif /* MAIN */
1447
1448 /* lib_mvcur.c ends here */