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: }

Defined functions

CalcIDCosts defined in line 367; used 2 times
CalcIDCosts1 defined in line 387; used 2 times
CalcLID defined in line 410; used 3 times
calculate_scrolling defined in line 95; used 1 times
  • in line 72
do_scrolling defined in line 222; used 1 times
  • in line 75
scrolling_1 defined in line 60; used 1 times
scrolling_max_lines_saved defined in line 301; used 1 times

Defined variables

DLcost defined in line 56; used 2 times
DLncost defined in line 58; used 2 times
ILcost defined in line 55; used 2 times
ILncost defined in line 57; used 2 times

Defined struct's

matrix_elt defined in line 35; used 12 times

Defined macros

INFINITY defined in line 33; used 6 times
max defined in line 26; never used
min defined in line 27; used 2 times
Last modified: 1985-11-23
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1298
Valid CSS Valid XHTML 1.0 Strict