Annotation of 43BSD/contrib/emacs/src/scroll.c, revision 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.