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