|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.