Annotation of 43BSD/contrib/emacs/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 Richard M. Stallman.
                      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 = j;
                    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:            cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
                    191:          }
                    192:        p->insertcost = min (cost, cost1) + draw_cost[i];
                    193:        p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
                    194: 
                    195:        /* Calculate the cost if we do a delete line after
                    196:           outputting this line.
                    197:           That is, we update through line i
                    198:           based on old lines through j-1,
                    199:           and throw away old line j.  */
                    200:        p1 = p - 1;             /* matrix [i, j-1] */
                    201:        /* No need to think about doing an insert followed
                    202:           immediately by a delete.  */
                    203:        if (free_at_end == i)
                    204:          {
                    205:            cost = p1->writecost;
                    206:            cost1 = p1->deletecost;
                    207:          }
                    208:        else
                    209:          {
                    210:            cost = p1->writecost + first_delete_cost[i];
                    211:            cost1 = p1->deletecost + next_delete_cost[i];
                    212:          }
                    213:        p->deletecost = min (cost, cost1);
                    214:        p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
                    215:       }
                    216: }
                    217: 
                    218: /* Perform insert-lines and delete-lines operations
                    219:  according to the costs in the matrix.
                    220:  Updates the contents of PhysScreen to record what was done. */
                    221: 
                    222: do_scrolling (matrix, window_size, unchanged_at_top)
                    223:      struct matrix_elt *matrix;
                    224:      int window_size;
                    225:      int unchanged_at_top;
                    226: {
                    227:   register struct matrix_elt *p;
                    228:   register int i, j;
                    229:   struct { int count, pos; } queue[MScreenLength];
                    230:   int offset = unchanged_at_top;
                    231:   int qi = 0;
                    232:   int window = 0;
                    233:   register int tem;
                    234:   extern struct display_line *PhysScreen[MScreenLength + 1];
                    235:   extern struct display_line *OPhysScreen[MScreenLength + 1];
                    236: 
                    237: /* First do all deletions of lines; queue up insertions.
                    238:   Also move lines to correct slots in PhysScreen */
                    239: 
                    240:   i = j = window_size;
                    241: 
                    242:   while (i > 0 || j > 0)
                    243:     {
                    244:       p = matrix + i * (window_size + 1) + j;
                    245:       tem = p->insertcost;
                    246:       if (tem < p->writecost && tem < p->deletecost)
                    247:        {
                    248:          /* Insert should be done at vpos i-1, plus maybe some before */
                    249:          queue[qi].count = p->insertcount;
                    250:          i -= p->insertcount;
                    251:          queue[qi++].pos = i + unchanged_at_top;
                    252:        }
                    253:       else if (p->deletecost < p->writecost)
                    254:        {
                    255:          /* Old line at vpos j-1, and maybe some before it,
                    256:             should be deleted */
                    257:          j -= p->deletecount;
                    258:          if (!window)
                    259:            {
                    260:              set_terminal_window (window_size + unchanged_at_top);
                    261:              window = 1;
                    262:            }
                    263:          ins_del_lines (j + unchanged_at_top, - p->deletecount);
                    264:        }
                    265:       else
                    266:        {
                    267:          /* Best thing done here is no insert or delete */
                    268:          /* Old line at vpos j-1 ends up at vpos i-1 */
                    269:          PhysScreen[i + offset] = OPhysScreen[j + offset];
                    270:          i--;
                    271:          j--;
                    272:        }
                    273:     }
                    274: 
                    275:   if (!window && qi)
                    276:     {
                    277:       set_terminal_window (window_size + unchanged_at_top);
                    278:       window = 1;
                    279:     }
                    280: 
                    281:   /* Now do all insertions */
                    282: 
                    283:   for (i = qi - 1; i >= 0; i--)
                    284:     {
                    285:       ins_del_lines (queue[i].pos, queue[i].count);
                    286:       /* mark the inserted lines as clear */
                    287:       tem = queue[i].pos;
                    288:       for (j = tem + queue[i].count; j > tem; j--)
                    289:        PhysScreen[j] = 0;
                    290:     }
                    291: 
                    292:   if (window)
                    293:     set_terminal_window (0);
                    294: }
                    295: 
                    296: /* Return number of lines in common between PhysScreen and DesiredScreen,
                    297:    considering only vpos range START to END (not including END).
                    298:    Ignores short lines (length < 20) on the assumption that
                    299:    avoiding redrawing such a line will have little weight.  */
                    300: 
                    301: int
                    302: scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
                    303:      int start, end;
                    304:      int *oldhash, *newhash, *cost;
                    305: {
                    306:   struct { int hash; int count; } lines[01000];
                    307:   register int i, h;
                    308:   register int matchcount = 0;
                    309: 
                    310:   bzero (lines, sizeof lines);
                    311: 
                    312:   /* Put new lines' hash codes in hash table.  */
                    313:   for (i = start; i < end; i++)
                    314:     {
                    315:       if (cost[i] > 20)
                    316:        {
                    317:          h = newhash[i] & 0777;
                    318:          lines[h].hash = newhash[i];
                    319:          lines[h].count++;
                    320:        }
                    321:     }
                    322: 
                    323:   /* Look up old line hash codes in the hash table.
                    324:      Count number of matches between old lines and new.  */
                    325: 
                    326:   for (i = start; i < end; i++)
                    327:     {
                    328:       h = oldhash[i] & 0777;
                    329:       if (oldhash[i] == lines[h].hash)
                    330:        {
                    331:          matchcount++;
                    332:          if (--lines[h].count == 0)
                    333:            lines[h].hash = 0;
                    334:        }
                    335:     }
                    336: 
                    337:   return matchcount;
                    338: }
                    339: 
                    340: /* Calculate the insert and delete line costs.
                    341: 
                    342:    We keep the ID costs in a precomputed array based on the position
                    343:    at which the I or D is performed.  Also, there are two kinds of ID
                    344:    costs: the "once-only" and the "repeated".  This is to handle both
                    345:    those terminals that are able to insert N lines at a time (once-
                    346:    only) and those that must repeatedly insert one line.
                    347: 
                    348:    The cost to insert N lines at line L is
                    349:            [tt.t_ILov  + (screen_height + 1 - L) * tt.t_ILpf] +
                    350:        N * [tt.t_ILnov + (screen_height + 1 - L) * tt.t_ILnpf]
                    351: 
                    352:    ILov represents the basic insert line overhead.  ILpf is the padding
                    353:    required to allow the terminal time to move a line: insertion at line
                    354:    L changes (screen_height + 1 - L) lines.
                    355: 
                    356:    The first bracketed expression above is the overhead; the second is
                    357:    the multiply factor.  Both are dependent only on the position at
                    358:    which the insert is performed.  We store the overhead in ILcost and
                    359:    the multiply factor in ILncost.  Note however that any insertion
                    360:    must include at least one multiply factor.  Rather than compute this
                    361:    as ILcost[line]+ILncost[line], we add ILncost into ILcost.  This is
                    362:    reasonable because of the particular algorithm used in calcM.
                    363: 
                    364:    Deletion is essentially the same as insertion.
                    365:  */
                    366: 
                    367: CalcIDCosts (ins_line_string, multi_ins_string,
                    368:             del_line_string, multi_del_string,
                    369:             setup_string, cleanup_string)
                    370:      char *ins_line_string, *multi_ins_string;
                    371:      char *del_line_string, *multi_del_string;
                    372:      char *setup_string, *cleanup_string;
                    373: {
                    374:   /* Discourage long scrolls slightly on fast lines.
                    375:      This says that scrolling nearly the full length of the screen
                    376:      is not worth it if reprinting takes less than 1/4 second.  */
                    377:   int extra = baud_rate / (10 * 4 * screen_height);
                    378: 
                    379:   CalcIDCosts1 (ins_line_string, multi_ins_string,
                    380:                setup_string, cleanup_string,
                    381:                ILcost, ILncost, extra);
                    382:   CalcIDCosts1 (del_line_string, multi_del_string,
                    383:                setup_string, cleanup_string,
                    384:                DLcost, DLncost, 0);
                    385: }
                    386: 
                    387: CalcIDCosts1 (one_line_string, multi_string,
                    388:              setup_string, cleanup_string,
                    389:              costvec, ncostvec, extra)
                    390:      char *one_line_string, *multi_string;
                    391:      char *setup_string, *cleanup_string;
                    392:      int *costvec, *ncostvec;
                    393:      int extra;
                    394: {
                    395:   if (multi_string)
                    396:     CalcLID (string_cost (multi_string),
                    397:             per_line_cost (multi_string),
                    398:             extra, 0, costvec, ncostvec);
                    399:   else if (one_line_string)
                    400:     CalcLID (string_cost (setup_string) + string_cost (cleanup_string), 0,
                    401:             string_cost (one_line_string) + extra,
                    402:             per_line_cost (one_line_string),
                    403:             costvec, ncostvec);
                    404:   else
                    405:     CalcLID (9999, 0, 9999, 0,
                    406:             costvec, ncostvec);
                    407: }
                    408: 
                    409: /* Calculate the line ID overhead and multiply factor values */
                    410: CalcLID (ov1, pf1, ovn, pfn, ov, mf)
                    411:      int ov1, ovn;
                    412:      int pf1, pfn;
                    413:      register int *ov, *mf;
                    414: {
                    415:   register int i;
                    416:   register int insert_overhead = ov1 * 10 + screen_height * pf1;
                    417:   register int next_insert_cost = ovn * 10 + screen_height * pfn;
                    418: 
                    419:   for (i = screen_height - 1; i > 0; i--)
                    420:     {
                    421:       *mf++ = next_insert_cost / 10;
                    422:       next_insert_cost -= pfn;
                    423:       *ov++ = (insert_overhead + next_insert_cost) / 10;
                    424:       insert_overhead -= pf1;
                    425:     }
                    426: }

unix.superglobalmegacorp.com

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