|
|
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.