Annotation of 43BSDReno/contrib/emacs-18.55/src/scroll.c, revision 1.1.1.1

1.1       root        1: /* Calculate what ins/del line to do, and do it, for Emacs redisplay.
                      2:    Copyright (C) 1985, 1986 Free Software Foundation, Inc.
                      3: 
                      4: This file is part of GNU Emacs.
                      5: 
                      6: GNU Emacs is distributed in the hope that it will be useful,
                      7: but WITHOUT ANY WARRANTY.  No author or distributor
                      8: accepts responsibility to anyone for the consequences of using it
                      9: or for whether it serves any particular purpose or works at all,
                     10: unless he says so in writing.  Refer to the GNU Emacs General Public
                     11: License for full details.
                     12: 
                     13: Everyone is granted permission to copy, modify and redistribute
                     14: GNU Emacs, but only under the conditions described in the
                     15: GNU Emacs General Public License.   A copy of this license is
                     16: supposed to have been given to you along with GNU Emacs so you
                     17: can know your rights and responsibilities.  It should be in a
                     18: file named COPYING.  Among other things, the copyright notice
                     19: and this notice must be preserved on all copies.  */
                     20: 
                     21: 
                     22: #include "config.h"
                     23: #include "termchar.h"
                     24: #include "dispextern.h"
                     25: 
                     26: #define max(a, b) ((a) > (b) ? (a) : (b))
                     27: #define min(a, b) ((a) < (b) ? (a) : (b))
                     28: 
                     29: /* All costs measured in characters.  Therefore, no cost
                     30:    can exceed MScreenLength * MScreenWidth (or so).
                     31:    That is about 10000, which fits in a short.  */
                     32: 
                     33: #define INFINITY 15000
                     34: 
                     35: struct matrix_elt
                     36:   {
                     37:     /* Cost of outputting through this line
                     38:        if no insert/delete is done just above it.  */
                     39:     short writecost;
                     40:     /* Cost of outputting through this line
                     41:        if an insert is done just above it.  */
                     42:     short insertcost;
                     43:     /* Cost of outputting through this line
                     44:        if a delete is done just above it.  */
                     45:     short deletecost;
                     46:     /* Number of inserts so far in this run of inserts,
                     47:        for the cost in insertcost.  */
                     48:     char insertcount;
                     49:     /* Number of deletes so far in this run of deletes,
                     50:        for the cost in deletecost.  */
                     51:     char deletecount;
                     52:   };
                     53: 
                     54: /* See CalcIDCosts for on the arrays below */
                     55: int ILcost[MScreenLength];/* ov(n) + 1*mf(n) */
                     56: int DLcost[MScreenLength];/* ov(n) + 1*mf(n) */
                     57: int ILncost[MScreenLength];/* mf(n) */
                     58: int DLncost[MScreenLength];/* mf(n) */
                     59: 
                     60: scrolling_1 (window_size, unchanged_at_top, unchanged_at_bottom,
                     61:             draw_cost, old_hash, new_hash, free_at_end)
                     62:      int window_size, unchanged_at_top, unchanged_at_bottom;
                     63:      int *draw_cost;
                     64:      int *old_hash;
                     65:      int *new_hash;
                     66:      int free_at_end;
                     67: {
                     68:   struct matrix_elt *matrix;
                     69:   matrix = ((struct matrix_elt *)
                     70:            alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
                     71: 
                     72:   calculate_scrolling (matrix, window_size, unchanged_at_bottom,
                     73:                       draw_cost, old_hash, new_hash,
                     74:                       free_at_end);
                     75:   do_scrolling (matrix, window_size, unchanged_at_top);
                     76: }
                     77: 
                     78: /* Determine, in matrix[i,j], the cost of updating the first j old lines
                     79:    into the first i new lines.
                     80:    This involves using insert or delete somewhere if i != j.
                     81:    For each matrix elements, three kinds of costs are recorded:
                     82:    the smallest cost that ends with an insert, the smallest
                     83:    cost that ends with a delete, and the smallest cost that
                     84:    ends with neither one.  These are kept separate because
                     85:    on some terminals the cost of doing an insert varies
                     86:    depending on whether one was just done, etc.  */
                     87: 
                     88: /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
                     89:    old_hash[VPOS] is the hash code of the old line at VPOS.
                     90:    new_hash[VPOS] is the hash code of the new line at VPOS.
                     91:    Note that these are not true screen vpos's, but relative
                     92:    to the place at which the first mismatch between old and
                     93:    new contents appears.  */
                     94: 
                     95: calculate_scrolling (matrix, window_size, lines_below,
                     96:                     draw_cost, old_hash, new_hash,
                     97:                     free_at_end)
                     98:      /* matrix is of size window_size + 1 on each side.  */
                     99:      struct matrix_elt *matrix;
                    100:      int window_size;
                    101:      int *draw_cost;
                    102:      int *old_hash;
                    103:      int *new_hash;
                    104:      int free_at_end;
                    105: {
                    106:   register int i, j;
                    107:   register struct matrix_elt *p, *p1;
                    108:   register int cost, cost1;
                    109: 
                    110:   int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below);
                    111:   int *first_insert_cost = &ILcost[screen_height - lines_moved];
                    112:   int *first_delete_cost = &DLcost[screen_height - lines_moved];
                    113:   int *next_insert_cost = &ILncost[screen_height - lines_moved];
                    114:   int *next_delete_cost = &DLncost[screen_height - lines_moved];
                    115: 
                    116:   /* initialize the top left corner of the matrix */
                    117:   matrix->writecost = 0;
                    118:   matrix->insertcost = INFINITY;
                    119:   matrix->deletecost = INFINITY;
                    120:   matrix->insertcount = 0;
                    121:   matrix->deletecount = 0;
                    122: 
                    123:   /* initialize the left edge of the matrix */
                    124:   cost = first_insert_cost[1] - next_insert_cost[1];
                    125:   for (i = 1; i <= window_size; i++)
                    126:     {
                    127:       p = matrix + i * (window_size + 1);
                    128:       cost += draw_cost[i] + next_insert_cost[i];
                    129:       p->insertcost = cost;
                    130:       p->writecost = INFINITY;
                    131:       p->deletecost = INFINITY;
                    132:       p->insertcount = i;
                    133:       p->deletecount = 0;
                    134:     }
                    135: 
                    136:   /* initialize the top edge of the matrix */
                    137:   cost = first_delete_cost[1] - next_delete_cost[1];
                    138:   for (j = 1; j <= window_size; j++)
                    139:     {
                    140:       cost += next_delete_cost[j];
                    141:       matrix[j].deletecost = cost;
                    142:       matrix[j].writecost = INFINITY;
                    143:       matrix[j].insertcost = INFINITY;
                    144:       matrix[j].deletecount = j;
                    145:       matrix[j].insertcount = 0;
                    146:     }
                    147: 
                    148:   /* `i' represents the vpos among new screen contents.
                    149:      `j' represents the vpos among the old screen contents.  */
                    150:   p = matrix + window_size + 2;        /* matrix [1, 1] */
                    151:   for (i = 1; i <= window_size; i++, p++)
                    152:     for (j = 1; j <= window_size; j++, p++)
                    153:       {
                    154:        /* p contains the address of matrix [i, j] */
                    155: 
                    156:        /* First calculate the cost assuming we do
                    157:           not insert or delete above this line.
                    158:           That is, if we update through line i-1
                    159:           based on old lines through j-1,
                    160:           and then just change old line j to new line i.  */
                    161:        p1 = p - window_size - 2; /* matrix [i-1, j-1] */
                    162:        cost = p1->writecost;
                    163:        if (cost > p1->insertcost)
                    164:          cost = p1->insertcost;
                    165:        if (cost > p1->deletecost)
                    166:          cost = p1->deletecost;
                    167:        if (old_hash[j] != new_hash[i])
                    168:          cost += draw_cost[i];
                    169:        p->writecost = cost;
                    170: 
                    171:        /* Calculate the cost if we do an insert-line
                    172:           before outputting this line.
                    173:           That is, we update through line i-1
                    174:           based on old lines through j,
                    175:           do an insert-line on line i,
                    176:           and then output line i from scratch,
                    177:           leaving old lines starting from j for reuse below.  */
                    178:        p1 = p - window_size - 1; /* matrix [i-1, j] */
                    179:        /* No need to think about doing a delete followed
                    180:           immediately by an insert.  It cannot be as good
                    181:           as not doing either of them.  */
                    182:        if (free_at_end == i)
                    183:          {
                    184:            cost = p1->writecost;
                    185:            cost1 = p1->insertcost;
                    186:          }
                    187:        else
                    188:          {
                    189:            cost = p1->writecost + first_insert_cost[i];
                    190:            if (p1->insertcount > i)
                    191:              abort ();
                    192:            cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
                    193:          }
                    194:        p->insertcost = min (cost, cost1) + draw_cost[i];
                    195:        p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
                    196:        if (p->insertcount > i)
                    197:          abort ();
                    198: 
                    199:        /* Calculate the cost if we do a delete line after
                    200:           outputting this line.
                    201:           That is, we update through line i
                    202:           based on old lines through j-1,
                    203:           and throw away old line j.  */
                    204:        p1 = p - 1;             /* matrix [i, j-1] */
                    205:        /* No need to think about doing an insert followed
                    206:           immediately by a delete.  */
                    207:        if (free_at_end == i)
                    208:          {
                    209:            cost = p1->writecost;
                    210:            cost1 = p1->deletecost;
                    211:          }
                    212:        else
                    213:          {
                    214:            cost = p1->writecost + first_delete_cost[i];
                    215:            cost1 = p1->deletecost + next_delete_cost[i];
                    216:          }
                    217:        p->deletecost = min (cost, cost1);
                    218:        p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
                    219:       }
                    220: }
                    221: 
                    222: /* Perform insert-lines and delete-lines operations
                    223:  according to the costs in the matrix.
                    224:  Updates the contents of PhysScreen to record what was done. */
                    225: 
                    226: do_scrolling (matrix, window_size, unchanged_at_top)
                    227:      struct matrix_elt *matrix;
                    228:      int window_size;
                    229:      int unchanged_at_top;
                    230: {
                    231:   register struct matrix_elt *p;
                    232:   register int i, j;
                    233:   struct { int count, pos; } queue[MScreenLength];
                    234:   int offset = unchanged_at_top;
                    235:   int qi = 0;
                    236:   int window = 0;
                    237:   register int tem;
                    238:   extern struct display_line *PhysScreen[MScreenLength + 1];
                    239:   extern struct display_line *OPhysScreen[MScreenLength + 1];
                    240: 
                    241: /* First do all deletions of lines; queue up insertions.
                    242:   Also move lines to correct slots in PhysScreen */
                    243: 
                    244:   i = j = window_size;
                    245: 
                    246:   while (i > 0 || j > 0)
                    247:     {
                    248:       p = matrix + i * (window_size + 1) + j;
                    249:       tem = p->insertcost;
                    250:       if (tem < p->writecost && tem < p->deletecost)
                    251:        {
                    252:          /* Insert should be done at vpos i-1, plus maybe some before */
                    253:          queue[qi].count = p->insertcount;
                    254:          i -= p->insertcount;
                    255:          queue[qi++].pos = i + unchanged_at_top;
                    256:        }
                    257:       else if (p->deletecost < p->writecost)
                    258:        {
                    259:          /* Old line at vpos j-1, and maybe some before it,
                    260:             should be deleted */
                    261:          j -= p->deletecount;
                    262:          if (!window)
                    263:            {
                    264:              set_terminal_window (window_size + unchanged_at_top);
                    265:              window = 1;
                    266:            }
                    267:          ins_del_lines (j + unchanged_at_top, - p->deletecount);
                    268:        }
                    269:       else
                    270:        {
                    271:          /* Best thing done here is no insert or delete */
                    272:          /* Old line at vpos j-1 ends up at vpos i-1 */
                    273:          PhysScreen[i + offset] = OPhysScreen[j + offset];
                    274:          i--;
                    275:          j--;
                    276:        }
                    277:     }
                    278: 
                    279:   if (!window && qi)
                    280:     {
                    281:       set_terminal_window (window_size + unchanged_at_top);
                    282:       window = 1;
                    283:     }
                    284: 
                    285:   /* Now do all insertions */
                    286: 
                    287:   for (i = qi - 1; i >= 0; i--)
                    288:     {
                    289:       ins_del_lines (queue[i].pos, queue[i].count);
                    290:       /* mark the inserted lines as clear */
                    291:       tem = queue[i].pos;
                    292:       for (j = tem + queue[i].count; j > tem; j--)
                    293:        PhysScreen[j] = 0;
                    294:     }
                    295: 
                    296:   if (window)
                    297:     set_terminal_window (0);
                    298: }
                    299: 
                    300: /* Return number of lines in common between PhysScreen and DesiredScreen,
                    301:    considering only vpos range START to END (not including END).
                    302:    Ignores short lines (length < 20) on the assumption that
                    303:    avoiding redrawing such a line will have little weight.  */
                    304: 
                    305: int
                    306: scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
                    307:      int start, end;
                    308:      int *oldhash, *newhash, *cost;
                    309: {
                    310:   struct { int hash; int count; } lines[01000];
                    311:   register int i, h;
                    312:   register int matchcount = 0;
                    313: 
                    314:   bzero (lines, sizeof lines);
                    315: 
                    316:   /* Put new lines' hash codes in hash table.  */
                    317:   for (i = start; i < end; i++)
                    318:     {
                    319:       if (cost[i] > 20)
                    320:        {
                    321:          h = newhash[i] & 0777;
                    322:          lines[h].hash = newhash[i];
                    323:          lines[h].count++;
                    324:        }
                    325:     }
                    326: 
                    327:   /* Look up old line hash codes in the hash table.
                    328:      Count number of matches between old lines and new.  */
                    329: 
                    330:   for (i = start; i < end; i++)
                    331:     {
                    332:       h = oldhash[i] & 0777;
                    333:       if (oldhash[i] == lines[h].hash)
                    334:        {
                    335:          matchcount++;
                    336:          if (--lines[h].count == 0)
                    337:            lines[h].hash = 0;
                    338:        }
                    339:     }
                    340: 
                    341:   return matchcount;
                    342: }
                    343: 
                    344: /* Return a measure of the cost of moving the lines
                    345:    starting with vpos FROM, up to but not including vpos TO,
                    346:    down by AMOUNT lines (AMOUNT may be negative).
                    347:    These are the same arguments that might be given to
                    348:    scroll_screen_lines to perform this scrolling.  */
                    349: 
                    350: scroll_cost (from, to, amount)
                    351:      int from, to, amount;
                    352: {
                    353:   /* Compute how many lines, at bottom of screen,
                    354:      will not be involved in actual motion.  */
                    355:   int ok_below = screen_height - to;
                    356:   if (amount > 0) ok_below -= amount;
                    357:   if (! scroll_region_ok) ok_below = 0;
                    358: 
                    359:   if (amount == 0)
                    360:     return 0;
                    361: 
                    362:   if (amount < 0)
                    363:     {
                    364:       int temp = to;
                    365:       to = from + amount;
                    366:       from = temp + amount;
                    367:       amount = - amount;
                    368:     }
                    369: 
                    370:   from += ok_below;
                    371:   to += ok_below;
                    372: 
                    373:   return ILcost[from] + (amount - 1) * ILncost[from]
                    374:     + DLcost[to] + (amount - 1) * DLncost[to];
                    375: }
                    376: 
                    377: /* Calculate the insert and delete line costs.
                    378: 
                    379:    We keep the ID costs in a precomputed array based on the position
                    380:    at which the I or D is performed.  Also, there are two kinds of ID
                    381:    costs: the "once-only" and the "repeated".  This is to handle both
                    382:    those terminals that are able to insert N lines at a time (once-
                    383:    only) and those that must repeatedly insert one line.
                    384: 
                    385:    The cost to insert N lines at line L is
                    386:            [tt.t_ILov  + (screen_height + 1 - L) * tt.t_ILpf] +
                    387:        N * [tt.t_ILnov + (screen_height + 1 - L) * tt.t_ILnpf]
                    388: 
                    389:    ILov represents the basic insert line overhead.  ILpf is the padding
                    390:    required to allow the terminal time to move a line: insertion at line
                    391:    L changes (screen_height + 1 - L) lines.
                    392: 
                    393:    The first bracketed expression above is the overhead; the second is
                    394:    the multiply factor.  Both are dependent only on the position at
                    395:    which the insert is performed.  We store the overhead in ILcost and
                    396:    the multiply factor in ILncost.  Note however that any insertion
                    397:    must include at least one multiply factor.  Rather than compute this
                    398:    as ILcost[line]+ILncost[line], we add ILncost into ILcost.  This is
                    399:    reasonable because of the particular algorithm used in calcM.
                    400: 
                    401:    Deletion is essentially the same as insertion.
                    402:  */
                    403: 
                    404: CalcIDCosts (ins_line_string, multi_ins_string,
                    405:             del_line_string, multi_del_string,
                    406:             setup_string, cleanup_string)
                    407:      char *ins_line_string, *multi_ins_string;
                    408:      char *del_line_string, *multi_del_string;
                    409:      char *setup_string, *cleanup_string;
                    410: {
                    411:   /* Discourage long scrolls slightly on fast lines.
                    412:      This says that scrolling nearly the full length of the screen
                    413:      is not worth it if reprinting takes less than 1/4 second.  */
                    414:   int extra = baud_rate / (10 * 4 * screen_height);
                    415: 
                    416:   CalcIDCosts1 (ins_line_string, multi_ins_string,
                    417:                setup_string, cleanup_string,
                    418:                ILcost, ILncost, extra);
                    419:   CalcIDCosts1 (del_line_string, multi_del_string,
                    420:                setup_string, cleanup_string,
                    421:                DLcost, DLncost, 0);
                    422: }
                    423: 
                    424: CalcIDCosts1 (one_line_string, multi_string,
                    425:              setup_string, cleanup_string,
                    426:              costvec, ncostvec, extra)
                    427:      char *one_line_string, *multi_string;
                    428:      char *setup_string, *cleanup_string;
                    429:      int *costvec, *ncostvec;
                    430:      int extra;
                    431: {
                    432:   if (multi_string)
                    433:     CalcLID (string_cost (multi_string),
                    434:             per_line_cost (multi_string),
                    435:             extra, 0, costvec, ncostvec);
                    436:   else if (one_line_string)
                    437:     CalcLID (string_cost (setup_string) + string_cost (cleanup_string), 0,
                    438:             string_cost (one_line_string) + extra,
                    439:             per_line_cost (one_line_string),
                    440:             costvec, ncostvec);
                    441:   else
                    442:     CalcLID (9999, 0, 9999, 0,
                    443:             costvec, ncostvec);
                    444: }
                    445: 
                    446: /* Calculate the line ID overhead and multiply factor values */
                    447: CalcLID (ov1, pf1, ovn, pfn, ov, mf)
                    448:      int ov1, ovn;
                    449:      int pf1, pfn;
                    450:      register int *ov, *mf;
                    451: {
                    452:   register int i;
                    453:   register int insert_overhead = ov1 * 10 + screen_height * pf1;
                    454:   register int next_insert_cost = ovn * 10 + screen_height * pfn;
                    455: 
                    456:   for (i = screen_height - 1; i > 0; i--)
                    457:     {
                    458:       *mf++ = next_insert_cost / 10;
                    459:       next_insert_cost -= pfn;
                    460:       *ov++ = (insert_overhead + next_insert_cost) / 10;
                    461:       insert_overhead -= pf1;
                    462:     }
                    463: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.