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