Annotation of GNUtools/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, 1986, 1990 Free Software Foundation, Inc.
                      3: 
                      4: This file is part of GNU Emacs.
                      5: 
                      6: GNU Emacs is free software; you can redistribute it and/or modify
                      7: it under the terms of the GNU General Public License as published by
                      8: the Free Software Foundation; either version 1, or (at your option)
                      9: any later version.
                     10: 
                     11: GNU Emacs is distributed in the hope that it will be useful,
                     12: but WITHOUT ANY WARRANTY; without even the implied warranty of
                     13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     14: GNU General Public License for more details.
                     15: 
                     16: You should have received a copy of the GNU General Public License
                     17: along with GNU Emacs; see the file COPYING.  If not, write to
                     18: the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
                     19: 
                     20: 
                     21: #include <stdio.h>
                     22: #include "config.h"
                     23: #include "termchar.h"
                     24: #include "termhooks.h"
                     25: #include "dispextern.h"
                     26: 
                     27: #define max(a, b) ((a) > (b) ? (a) : (b))
                     28: #define min(a, b) ((a) < (b) ? (a) : (b))
                     29: 
                     30: struct matrix_elt
                     31:   {
                     32:     /* Cost of outputting through this line
                     33:        if no insert/delete is done just above it.  */
                     34:     int writecost;
                     35:     /* Cost of outputting through this line
                     36:        if an insert is done just above it.  */
                     37:     int insertcost;
                     38:     /* Cost of outputting through this line
                     39:        if a delete is done just above it.  */
                     40:     int deletecost;
                     41:     /* Number of inserts so far in this run of inserts,
                     42:        for the cost in insertcost.  */
                     43:     char insertcount;
                     44:     /* Number of deletes so far in this run of deletes,
                     45:        for the cost in deletecost.  */
                     46:     char deletecount;
                     47:   };
                     48: 
                     49: /* This exceeds the sum of any reasonable number of INFINITY's.  */
                     50: #define SUPER_INFINITY (1000 * INFINITY)
                     51: 
                     52: /* See CalcIDCosts for on the arrays below */
                     53: int *ILcost;
                     54: int *DLcost;
                     55: int *ILncost;
                     56: int *DLncost;
                     57: 
                     58: scrolling_1 (window_size, unchanged_at_top, unchanged_at_bottom,
                     59:             draw_cost, old_hash, new_hash, free_at_end)
                     60:      int window_size, unchanged_at_top, unchanged_at_bottom;
                     61:      int *draw_cost;
                     62:      int *old_hash;
                     63:      int *new_hash;
                     64:      int free_at_end;
                     65: {
                     66:   struct matrix_elt *matrix;
                     67:   matrix = ((struct matrix_elt *)
                     68:            alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
                     69: 
                     70:   calculate_scrolling (matrix, window_size, unchanged_at_bottom,
                     71:                       draw_cost, old_hash, new_hash,
                     72:                       free_at_end);
                     73:   do_scrolling (matrix, window_size, unchanged_at_top);
                     74: }
                     75: 
                     76: /* Determine, in matrix[i,j], the cost of updating the first j old lines
                     77:    into the first i new lines.
                     78:    This involves using insert or delete somewhere if i != j.
                     79:    For each matrix elements, three kinds of costs are recorded:
                     80:    the smallest cost that ends with an insert, the smallest
                     81:    cost that ends with a delete, and the smallest cost that
                     82:    ends with neither one.  These are kept separate because
                     83:    on some terminals the cost of doing an insert varies
                     84:    depending on whether one was just done, etc.  */
                     85: 
                     86: /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
                     87:    old_hash[VPOS] is the hash code of the old line at VPOS.
                     88:    new_hash[VPOS] is the hash code of the new line at VPOS.
                     89:    Note that these are not true screen vpos's, but relative
                     90:    to the place at which the first mismatch between old and
                     91:    new contents appears.  */
                     92: 
                     93: calculate_scrolling (matrix, window_size, lines_below,
                     94:                     draw_cost, old_hash, new_hash,
                     95:                     free_at_end)
                     96:      /* matrix is of size window_size + 1 on each side.  */
                     97:      struct matrix_elt *matrix;
                     98:      int window_size;
                     99:      int *draw_cost;
                    100:      int *old_hash;
                    101:      int *new_hash;
                    102:      int free_at_end;
                    103: {
                    104:   register int i, j;
                    105:   register struct matrix_elt *p, *p1;
                    106:   register int cost, cost1;
                    107: 
                    108:   int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below);
                    109:   /* We subtract 1 to compensate for the fact that i and j have values
                    110:      starting with 1.  */
                    111:   int *first_insert_cost = &ILcost[screen_height - lines_moved - 1];
                    112:   int *first_delete_cost = &DLcost[screen_height - lines_moved - 1];
                    113:   int *next_insert_cost = &ILncost[screen_height - lines_moved - 1];
                    114:   int *next_delete_cost = &DLncost[screen_height - lines_moved - 1];
                    115: 
                    116:   /* initialize the top left corner of the matrix */
                    117:   matrix->writecost = 0;
                    118:   matrix->insertcost = SUPER_INFINITY;
                    119:   matrix->deletecost = SUPER_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 = SUPER_INFINITY;
                    131:       p->deletecost = SUPER_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 = SUPER_INFINITY;
                    143:       matrix[j].insertcost = SUPER_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 current_screen 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 queue { int count, pos; } *queue;
                    234:   int offset = unchanged_at_top;
                    235:   int qi = 0;
                    236:   int window = 0;
                    237:   register int tem;
                    238:   int next;
                    239: 
                    240:   queue = (struct queue *) alloca (screen_height * sizeof (struct queue));
                    241: 
                    242:   bcopy (current_screen->contents, temp_screen->contents,
                    243:         current_screen->height * sizeof (char *));
                    244:   bcopy (current_screen->used, temp_screen->used,
                    245:         current_screen->height * sizeof (int));
                    246:   bcopy (current_screen->highlight, temp_screen->highlight,
                    247:         current_screen->height);
                    248:   bzero (temp_screen->enable, temp_screen->height);
                    249: 
                    250: /* First do all deletions of lines; queue up insertions.
                    251:    Also move lines to correct slots in current_screen.  */
                    252: 
                    253:   i = j = window_size;
                    254: 
                    255:   while (i > 0 || j > 0)
                    256:     {
                    257:       p = matrix + i * (window_size + 1) + j;
                    258:       tem = p->insertcost;
                    259:       if (tem < p->writecost && tem < p->deletecost)
                    260:        {
                    261:          /* Insert should be done at vpos i-1, plus maybe some before */
                    262:          queue[qi].count = p->insertcount;
                    263:          i -= p->insertcount;
                    264:          queue[qi++].pos = i + unchanged_at_top;
                    265:        }
                    266:       else if (p->deletecost < p->writecost)
                    267:        {
                    268:          /* Old line at vpos j-1, and maybe some before it,
                    269:             should be deleted */
                    270:          j -= p->deletecount;
                    271:          if (!window)
                    272:            {
                    273:              set_terminal_window (window_size + unchanged_at_top);
                    274:              window = 1;
                    275:            }
                    276:          ins_del_lines (j + unchanged_at_top, - p->deletecount);
                    277:        }
                    278:       else
                    279:        {
                    280:          /* Best thing done here is no insert or delete */
                    281:          /* Old line at vpos j-1 ends up at vpos i-1 */
                    282:          current_screen->contents[i + offset - 1]
                    283:            = temp_screen->contents[j + offset - 1];
                    284:          current_screen->used[i + offset - 1]
                    285:            = temp_screen->used[j + offset - 1];
                    286:          current_screen->highlight[i + offset - 1]
                    287:            = temp_screen->highlight[j + offset - 1];
                    288: 
                    289:          temp_screen->enable[j + offset - 1] = 1;
                    290:          i--;
                    291:          j--;
                    292:        }
                    293:     }
                    294: 
                    295:   if (!window && qi)
                    296:     {
                    297:       set_terminal_window (window_size + unchanged_at_top);
                    298:       window = 1;
                    299:     }
                    300: 
                    301:   /* Now do all insertions */
                    302: 
                    303:   next = unchanged_at_top;
                    304:   for (i = qi - 1; i >= 0; i--)
                    305:     {
                    306:       ins_del_lines (queue[i].pos, queue[i].count);
                    307:       /* Mark the inserted lines as clear,
                    308:         and put into them the line-contents strings
                    309:         that were discarded during the deletions.
                    310:         Those are the ones for which temp_screen->enable was not set.  */
                    311:       tem = queue[i].pos;
                    312:       for (j = tem + queue[i].count - 1; j >= tem; j--)
                    313:        {
                    314:          current_screen->enable[j] = 0;
                    315:          while (temp_screen->enable[next]) next++;
                    316:          current_screen->contents[j] = temp_screen->contents[next++];
                    317:        }
                    318:     }
                    319: 
                    320:   if (window)
                    321:     set_terminal_window (0);
                    322: }
                    323: 
                    324: /* Return number of lines in common between current screen contents
                    325:    and the text to be displayed,
                    326:    considering only vpos range START to END (not including END).
                    327:    Ignores short lines (length < 20) on the assumption that
                    328:    avoiding redrawing such a line will have little weight.  */
                    329: 
                    330: int
                    331: scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
                    332:      int start, end;
                    333:      int *oldhash, *newhash, *cost;
                    334: {
                    335:   struct { int hash; int count; } lines[01000];
                    336:   register int i, h;
                    337:   register int matchcount = 0;
                    338: 
                    339:   bzero (lines, sizeof lines);
                    340: 
                    341:   /* Put new lines' hash codes in hash table.  */
                    342:   for (i = start; i < end; i++)
                    343:     {
                    344:       if (cost[i] > 20)
                    345:        {
                    346:          h = newhash[i] & 0777;
                    347:          lines[h].hash = newhash[i];
                    348:          lines[h].count++;
                    349:        }
                    350:     }
                    351: 
                    352:   /* Look up old line hash codes in the hash table.
                    353:      Count number of matches between old lines and new.  */
                    354: 
                    355:   for (i = start; i < end; i++)
                    356:     {
                    357:       h = oldhash[i] & 0777;
                    358:       if (oldhash[i] == lines[h].hash)
                    359:        {
                    360:          matchcount++;
                    361:          if (--lines[h].count == 0)
                    362:            lines[h].hash = 0;
                    363:        }
                    364:     }
                    365: 
                    366:   return matchcount;
                    367: }
                    368: 
                    369: /* Return a measure of the cost of moving the lines
                    370:    starting with vpos FROM, up to but not including vpos TO,
                    371:    down by AMOUNT lines (AMOUNT may be negative).
                    372:    These are the same arguments that might be given to
                    373:    scroll_screen_lines to perform this scrolling.  */
                    374: 
                    375: scroll_cost (from, to, amount)
                    376:      int from, to, amount;
                    377: {
                    378:   /* Compute how many lines, at bottom of screen,
                    379:      will not be involved in actual motion.  */
                    380:   int ok_below = screen_height - to;
                    381:   if (amount > 0) ok_below -= amount;
                    382:   if (! scroll_region_ok) ok_below = 0;
                    383: 
                    384:   if (amount == 0)
                    385:     return 0;
                    386: 
                    387:   if (amount < 0)
                    388:     {
                    389:       int temp = to;
                    390:       to = from + amount;
                    391:       from = temp + amount;
                    392:       amount = - amount;
                    393:     }
                    394: 
                    395:   from += ok_below;
                    396:   to += ok_below;
                    397: 
                    398:   return (ILcost[from] + (amount - 1) * ILncost[from]
                    399:          + DLcost[to] + (amount - 1) * DLncost[to]);
                    400: }
                    401: 
                    402: /* Calculate the insert and delete line costs.
                    403: 
                    404:    We keep the ID costs in a precomputed array based on the position
                    405:    at which the I or D is performed.  Also, there are two kinds of ID
                    406:    costs: the "once-only" and the "repeated".  This is to handle both
                    407:    those terminals that are able to insert N lines at a time (once-
                    408:    only) and those that must repeatedly insert one line.
                    409: 
                    410:    The cost to insert N lines at line L is
                    411:            [tt.t_ILov  + (screen_height + 1 - L) * tt.t_ILpf] +
                    412:        N * [tt.t_ILnov + (screen_height + 1 - L) * tt.t_ILnpf]
                    413: 
                    414:    ILov represents the basic insert line overhead.  ILpf is the padding
                    415:    required to allow the terminal time to move a line: insertion at line
                    416:    L changes (screen_height + 1 - L) lines.
                    417: 
                    418:    The first bracketed expression above is the overhead; the second is
                    419:    the multiply factor.  Both are dependent only on the position at
                    420:    which the insert is performed.  We store the overhead in ILcost and
                    421:    the multiply factor in ILncost.  Note however that any insertion
                    422:    must include at least one multiply factor.  Rather than compute this
                    423:    as ILcost[line]+ILncost[line], we add ILncost into ILcost.  This is
                    424:    reasonable because of the particular algorithm used in calcM.
                    425: 
                    426:    Deletion is essentially the same as insertion.
                    427:  */
                    428: 
                    429: CalcIDCosts (ins_line_string, multi_ins_string,
                    430:             del_line_string, multi_del_string,
                    431:             setup_string, cleanup_string)
                    432:      char *ins_line_string, *multi_ins_string;
                    433:      char *del_line_string, *multi_del_string;
                    434:      char *setup_string, *cleanup_string;
                    435: {
                    436:   /* Discourage long scrolls slightly on fast lines.
                    437:      This says that scrolling nearly the full length of the screen
                    438:      is not worth it if reprinting takes less than 1/4 second.  */
                    439:   int extra = baud_rate / (10 * 4 * screen_height);
                    440: 
                    441:   if (ILcost != 0)
                    442:     {
                    443:       ILcost = (int *) xrealloc (ILcost, screen_height * sizeof (int));
                    444:       DLcost = (int *) xrealloc (DLcost, screen_height * sizeof (int));
                    445:       ILncost = (int *) xrealloc (ILncost, screen_height * sizeof (int));
                    446:       DLncost = (int *) xrealloc (DLncost, screen_height * sizeof (int));
                    447:     }
                    448:   else
                    449:     {
                    450:       ILcost = (int *) xmalloc (screen_height * sizeof (int));
                    451:       DLcost = (int *) xmalloc (screen_height * sizeof (int));
                    452:       ILncost = (int *) xmalloc (screen_height * sizeof (int));
                    453:       DLncost = (int *) xmalloc (screen_height * sizeof (int));
                    454:     }
                    455: 
                    456:   CalcIDCosts1 (ins_line_string, multi_ins_string,
                    457:                setup_string, cleanup_string,
                    458:                ILcost, ILncost, extra);
                    459:   CalcIDCosts1 (del_line_string, multi_del_string,
                    460:                setup_string, cleanup_string,
                    461:                DLcost, DLncost, 0);
                    462: }
                    463: 
                    464: CalcIDCosts1 (one_line_string, multi_string,
                    465:              setup_string, cleanup_string,
                    466:              costvec, ncostvec, extra)
                    467:      char *one_line_string, *multi_string;
                    468:      char *setup_string, *cleanup_string;
                    469:      int *costvec, *ncostvec;
                    470:      int extra;
                    471: {
                    472:   if (calculate_costs_hook)
                    473:     (*calculate_costs_hook) (extra, costvec, ncostvec);
                    474:   else if (dont_calculate_costs)
                    475:     CalcLID (0, 0, 0, 0, costvec, ncostvec);
                    476:   else if (multi_string)
                    477:     CalcLID (string_cost (multi_string),
                    478:             per_line_cost (multi_string),
                    479:             extra, 0, costvec, ncostvec);
                    480:   else if (one_line_string)
                    481:     CalcLID (string_cost (setup_string) + string_cost (cleanup_string), 0,
                    482:             string_cost (one_line_string) + extra,
                    483:             per_line_cost (one_line_string),
                    484:             costvec, ncostvec);
                    485:   else
                    486:     CalcLID (9999, 0, 9999, 0,
                    487:             costvec, ncostvec);
                    488: }
                    489: 
                    490: /* Calculate the line ID overhead and multiply factor values */
                    491: CalcLID (ov1, pf1, ovn, pfn, ov, mf)
                    492:      int ov1, ovn;
                    493:      int pf1, pfn;
                    494:      register int *ov, *mf;
                    495: {
                    496:   register int i;
                    497:   register int insert_overhead = ov1 * 10 + screen_height * pf1;
                    498:   register int next_insert_cost = ovn * 10 + screen_height * pfn;
                    499: 
                    500:   for (i = 0; i < screen_height; i++)
                    501:     {
                    502:       *mf++ = next_insert_cost / 10;
                    503:       next_insert_cost -= pfn;
                    504:       *ov++ = (insert_overhead + next_insert_cost) / 10;
                    505:       insert_overhead -= pf1;
                    506:     }
                    507: }

unix.superglobalmegacorp.com

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