1 /* Calculate what line insertion or deletion to do, and do it,
2 Copyright (C) 1985, 1986, 1990, 1992 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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 2, or (at your option)
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.
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. */
24 #include "dispextern.h"
27 extern struct display_line
**ophys_lines
;
29 #define max(a, b) ((a) > (b) ? (a) : (b))
30 #define min(a, b) ((a) < (b) ? (a) : (b))
32 /* All costs measured in characters.
33 So no cost can exceed the area of a screen, measured in characters.
34 Let's hope this is never more than 15000 characters. */
36 #define INFINITY 15000
40 /* Cost of outputting through this line
41 if no insert/delete is done just above it. */
43 /* Cost of outputting through this line
44 if an insert is done just above it. */
46 /* Cost of outputting through this line
47 if a delete is done just above it. */
49 /* Number of inserts so far in this run of inserts,
50 for the cost in insertcost. */
52 /* Number of deletes so far in this run of deletes,
53 for the cost in deletecost. */
58 /* Determine, in matrix[i,j], the cost of updating the first j old lines
59 into the first i new lines.
60 This involves using insert or delete somewhere if i != j.
61 For each matrix elements, three kinds of costs are recorded:
62 the smallest cost that ends with an insert, the smallest
63 cost that ends with a delete, and the smallest cost that
64 ends with neither one. These are kept separate because
65 on some terminals the cost of doing an insert varies
66 depending on whether one was just done, etc. */
68 /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
69 old_hash[VPOS] is the hash code of the old line at VPOS.
70 new_hash[VPOS] is the hash code of the new line at VPOS.
71 Note that these are not true screen vpos's, but relative
72 to the place at which the first mismatch between old and
73 new contents appears. */
76 calculate_scrolling (screen
, matrix
, window_size
, lines_below
,
77 draw_cost
, old_hash
, new_hash
,
80 /* matrix is of size window_size + 1 on each side. */
81 struct matrix_elt
*matrix
;
89 int screen_height
= SCREEN_HEIGHT (screen
);
90 register struct matrix_elt
*p
, *p1
;
91 register int cost
, cost1
;
93 int lines_moved
= window_size
+ (scroll_region_ok
? 0 : lines_below
);
94 /* first_insert_cost[I] is the cost of doing the first insert-line
95 at the I'th line of the lines we are considering,
96 where I is origin 1 (as it is below). */
97 int *first_insert_cost
98 = &SCREEN_INSERT_COST (screen
)[screen_height
- 1 - lines_moved
];
99 int *first_delete_cost
100 = &SCREEN_DELETE_COST (screen
)[screen_height
- 1 - lines_moved
];
101 int *next_insert_cost
102 = &SCREEN_INSERTN_COST (screen
)[screen_height
- 1 - lines_moved
];
103 int *next_delete_cost
104 = &SCREEN_DELETEN_COST (screen
)[screen_height
- 1 - lines_moved
];
106 /* Discourage long scrolls on fast lines.
107 Don't scroll nearly a full screen height unless it saves
108 at least 1/4 second. */
109 int extra_cost
= baud_rate
/ (10 * 4 * SCREEN_HEIGHT (screen
));
111 /* initialize the top left corner of the matrix */
112 matrix
->writecost
= 0;
113 matrix
->insertcost
= INFINITY
;
114 matrix
->deletecost
= INFINITY
;
115 matrix
->insertcount
= 0;
116 matrix
->deletecount
= 0;
118 /* initialize the left edge of the matrix */
119 cost
= first_insert_cost
[1] - next_insert_cost
[1];
120 for (i
= 1; i
<= window_size
; i
++)
122 p
= matrix
+ i
* (window_size
+ 1);
123 cost
+= draw_cost
[i
] + next_insert_cost
[i
] + extra_cost
;
124 p
->insertcost
= cost
;
125 p
->writecost
= INFINITY
;
126 p
->deletecost
= INFINITY
;
131 /* initialize the top edge of the matrix */
132 cost
= first_delete_cost
[1] - next_delete_cost
[1];
133 for (j
= 1; j
<= window_size
; j
++)
135 cost
+= next_delete_cost
[j
];
136 matrix
[j
].deletecost
= cost
;
137 matrix
[j
].writecost
= INFINITY
;
138 matrix
[j
].insertcost
= INFINITY
;
139 matrix
[j
].deletecount
= j
;
140 matrix
[j
].insertcount
= 0;
143 /* `i' represents the vpos among new screen contents.
144 `j' represents the vpos among the old screen contents. */
145 p
= matrix
+ window_size
+ 2; /* matrix [1, 1] */
146 for (i
= 1; i
<= window_size
; i
++, p
++)
147 for (j
= 1; j
<= window_size
; j
++, p
++)
149 /* p contains the address of matrix [i, j] */
151 /* First calculate the cost assuming we do
152 not insert or delete above this line.
153 That is, if we update through line i-1
154 based on old lines through j-1,
155 and then just change old line j to new line i. */
156 p1
= p
- window_size
- 2; /* matrix [i-1, j-1] */
157 cost
= p1
->writecost
;
158 if (cost
> p1
->insertcost
)
159 cost
= p1
->insertcost
;
160 if (cost
> p1
->deletecost
)
161 cost
= p1
->deletecost
;
162 if (old_hash
[j
] != new_hash
[i
])
163 cost
+= draw_cost
[i
];
166 /* Calculate the cost if we do an insert-line
167 before outputting this line.
168 That is, we update through line i-1
169 based on old lines through j,
170 do an insert-line on line i,
171 and then output line i from scratch,
172 leaving old lines starting from j for reuse below. */
173 p1
= p
- window_size
- 1; /* matrix [i-1, j] */
174 /* No need to think about doing a delete followed
175 immediately by an insert. It cannot be as good
176 as not doing either of them. */
177 if (free_at_end
== i
)
179 cost
= p1
->writecost
;
180 cost1
= p1
->insertcost
;
184 cost
= p1
->writecost
+ first_insert_cost
[i
];
185 if (p1
->insertcount
> i
)
187 cost1
= p1
->insertcost
+ next_insert_cost
[i
- p1
->insertcount
];
189 p
->insertcost
= min (cost
, cost1
) + draw_cost
[i
] + extra_cost
;
190 p
->insertcount
= (cost
< cost1
) ? 1 : p1
->insertcount
+ 1;
191 if (p
->insertcount
> i
)
194 /* Calculate the cost if we do a delete line after
195 outputting this line.
196 That is, we update through line i
197 based on old lines through j-1,
198 and throw away old line j. */
199 p1
= p
- 1; /* matrix [i, j-1] */
200 /* No need to think about doing an insert followed
201 immediately by a delete. */
202 if (free_at_end
== i
)
204 cost
= p1
->writecost
;
205 cost1
= p1
->deletecost
;
209 cost
= p1
->writecost
+ first_delete_cost
[i
];
210 cost1
= p1
->deletecost
+ next_delete_cost
[i
];
212 p
->deletecost
= min (cost
, cost1
);
213 p
->deletecount
= (cost
< cost1
) ? 1 : p1
->deletecount
+ 1;
217 /* Perform insert-lines and delete-lines operations
218 according to the costs in the matrix.
219 Updates the contents of the screen to record what was done. */
222 do_scrolling (screen
, matrix
, window_size
, unchanged_at_top
)
224 struct matrix_elt
*matrix
;
226 int unchanged_at_top
;
228 register struct matrix_elt
*p
;
230 register struct screen_glyphs
*current_screen
;
231 /* temp_screen->enable[i] means line i has been moved to current_screen. */
232 register struct screen_glyphs
*temp_screen
;
233 struct queue
{ int count
, pos
; } *queue
;
234 int offset
= unchanged_at_top
;
240 queue
= (struct queue
*) alloca (SCREEN_HEIGHT (screen
)
241 * sizeof (struct queue
));
243 current_screen
= SCREEN_CURRENT_GLYPHS (screen
);
244 temp_screen
= SCREEN_TEMP_GLYPHS (screen
);
246 bcopy (current_screen
->glyphs
, temp_screen
->glyphs
,
247 current_screen
->height
* sizeof (GLYPH
*));
248 bcopy (current_screen
->used
, temp_screen
->used
,
249 current_screen
->height
* sizeof (int));
250 bcopy (current_screen
->highlight
, temp_screen
->highlight
,
251 current_screen
->height
* sizeof (char));
252 bzero (temp_screen
->enable
, temp_screen
->height
* sizeof (char));
253 bcopy (current_screen
->bufp
, temp_screen
->bufp
,
254 current_screen
->height
* sizeof (int));
256 #ifdef HAVE_X_WINDOWS
257 if (SCREEN_IS_X (screen
))
259 bcopy (current_screen
->nruns
, temp_screen
->nruns
,
260 current_screen
->height
* sizeof (int));
261 bcopy (current_screen
->face_list
, temp_screen
->face_list
,
262 current_screen
->height
* sizeof (struct run
*));
263 bcopy (current_screen
->top_left_x
, temp_screen
->top_left_x
,
264 current_screen
->height
* sizeof (short));
265 bcopy (current_screen
->top_left_y
, temp_screen
->top_left_y
,
266 current_screen
->height
* sizeof (short));
267 bcopy (current_screen
->pix_width
, temp_screen
->pix_width
,
268 current_screen
->height
* sizeof (short));
269 bcopy (current_screen
->pix_height
, temp_screen
->pix_height
,
270 current_screen
->height
* sizeof (short));
276 while (i
> 0 || j
> 0)
278 p
= matrix
+ i
* (window_size
+ 1) + j
;
280 if (tem
< p
->writecost
&& tem
< p
->deletecost
)
282 /* Insert should be done at vpos i-1, plus maybe some before */
283 queue
[qi
].count
= p
->insertcount
;
285 queue
[qi
++].pos
= i
+ unchanged_at_top
;
287 else if (p
->deletecost
< p
->writecost
)
289 /* Old line at vpos j-1, and maybe some before it,
294 set_terminal_window (window_size
+ unchanged_at_top
);
297 ins_del_lines (j
+ unchanged_at_top
, - p
->deletecount
);
301 /* Best thing done here is no insert or delete */
302 /* Old line at vpos j-1 ends up at vpos i-1 */
303 current_screen
->glyphs
[i
+ offset
- 1]
304 = temp_screen
->glyphs
[j
+ offset
- 1];
305 current_screen
->used
[i
+ offset
- 1]
306 = temp_screen
->used
[j
+ offset
- 1];
307 current_screen
->highlight
[i
+ offset
- 1]
308 = temp_screen
->highlight
[j
+ offset
- 1];
310 temp_screen
->enable
[j
+ offset
- 1] = 1;
318 set_terminal_window (window_size
+ unchanged_at_top
);
322 /* Now do all insertions */
324 next
= unchanged_at_top
;
325 for (i
= qi
- 1; i
>= 0; i
--)
327 ins_del_lines (queue
[i
].pos
, queue
[i
].count
);
329 /* Mark the inserted lines as clear,
330 and put into them the line-contents strings
331 that were discarded during the deletions.
332 Those are the ones for which temp_screen->enable was not set. */
334 for (j
= tem
+ queue
[i
].count
- 1; j
>= tem
; j
--)
336 current_screen
->enable
[j
] = 0;
337 while (temp_screen
->enable
[next
])
339 current_screen
->glyphs
[j
] = temp_screen
->glyphs
[next
++];
344 set_terminal_window (0);
348 scrolling_1 (screen
, window_size
, unchanged_at_top
, unchanged_at_bottom
,
349 draw_cost
, old_hash
, new_hash
, free_at_end
)
351 int window_size
, unchanged_at_top
, unchanged_at_bottom
;
357 struct matrix_elt
*matrix
;
358 matrix
= ((struct matrix_elt
*)
359 alloca ((window_size
+ 1) * (window_size
+ 1) * sizeof *matrix
));
361 calculate_scrolling (screen
, matrix
, window_size
, unchanged_at_bottom
,
362 draw_cost
, old_hash
, new_hash
,
364 do_scrolling (screen
, matrix
, window_size
, unchanged_at_top
);
367 /* Return number of lines in common between current and desired screen contents
368 described to us only as vectors of hash codes OLDHASH and NEWHASH.
369 Consider only vpos range START to END (not including END).
370 Ignore short lines on the assumption that
371 avoiding redrawing such a line will have little weight. */
374 scrolling_max_lines_saved (start
, end
, oldhash
, newhash
, cost
)
376 int *oldhash
, *newhash
, *cost
;
378 struct { int hash
; int count
; } lines
[01000];
380 register int matchcount
= 0;
384 /* Compute a threshold which is 1/4 of average length of these lines. */
386 for (i
= start
; i
< end
; i
++)
387 avg_length
+= cost
[i
];
389 avg_length
/= end
- start
;
390 threshold
= avg_length
/ 4;
392 bzero (lines
, sizeof lines
);
394 /* Put new lines' hash codes in hash table.
395 Ignore lines shorter than the threshold.
396 Thus, if the lines that are in common
397 are mainly the ones that are short,
399 for (i
= start
; i
< end
; i
++)
401 if (cost
[i
] > threshold
)
403 h
= newhash
[i
] & 0777;
404 lines
[h
].hash
= newhash
[i
];
409 /* Look up old line hash codes in the hash table.
410 Count number of matches between old lines and new. */
412 for (i
= start
; i
< end
; i
++)
414 h
= oldhash
[i
] & 0777;
415 if (oldhash
[i
] == lines
[h
].hash
)
418 if (--lines
[h
].count
== 0)
426 /* Return a measure of the cost of moving the lines
427 starting with vpos FROM, up to but not including vpos TO,
428 down by AMOUNT lines (AMOUNT may be negative).
429 These are the same arguments that might be given to
430 scroll_screen_lines to perform this scrolling. */
432 scroll_cost (screen
, from
, to
, amount
)
434 int from
, to
, amount
;
436 /* Compute how many lines, at bottom of screen,
437 will not be involved in actual motion. */
440 int height
= SCREEN_HEIGHT (screen
);
445 if (! scroll_region_ok
)
454 from
= temp
+ amount
;
458 offset
= height
- limit
;
461 (SCREEN_INSERT_COST (screen
)[offset
+ from
]
462 + (amount
- 1) * SCREEN_INSERTN_COST (screen
)[offset
+ from
]
463 + SCREEN_DELETEN_COST (screen
)[offset
+ to
]
464 + (amount
- 1) * SCREEN_DELETE_COST (screen
)[offset
+ to
]);
467 /* Calculate the line insertion/deletion
468 overhead and multiply factor values */
471 line_ins_del (screen
, ov1
, pf1
, ovn
, pfn
, ov
, mf
)
475 register int *ov
, *mf
;
478 register int screen_height
= SCREEN_HEIGHT (screen
);
479 register int insert_overhead
= ov1
* 10;
480 register int next_insert_cost
= ovn
* 10;
482 for (i
= screen_height
-1; i
>= 0; i
--)
484 mf
[i
] = next_insert_cost
/ 10;
485 next_insert_cost
+= pfn
;
486 ov
[i
] = (insert_overhead
+ next_insert_cost
) / 10;
487 insert_overhead
+= pf1
;
492 ins_del_costs (screen
,
493 one_line_string
, multi_string
,
494 setup_string
, cleanup_string
,
495 costvec
, ncostvec
, coefficient
)
497 char *one_line_string
, *multi_string
;
498 char *setup_string
, *cleanup_string
;
499 int *costvec
, *ncostvec
;
503 line_ins_del (screen
,
504 string_cost (multi_string
) * coefficient
,
505 per_line_cost (multi_string
) * coefficient
,
506 0, 0, costvec
, ncostvec
);
507 else if (one_line_string
)
508 line_ins_del (screen
,
509 string_cost (setup_string
) + string_cost (cleanup_string
), 0,
510 string_cost (one_line_string
),
511 per_line_cost (one_line_string
),
514 line_ins_del (screen
,
519 /* Calculate the insert and delete line costs.
520 Note that this is done even when running with a window system
521 because we want to know how long scrolling takes (and avoid it).
522 This must be redone whenever the screen height changes.
524 We keep the ID costs in a precomputed array based on the position
525 at which the I or D is performed. Also, there are two kinds of ID
526 costs: the "once-only" and the "repeated". This is to handle both
527 those terminals that are able to insert N lines at a time (once-
528 only) and those that must repeatedly insert one line.
530 The cost to insert N lines at line L is
531 [tt.t_ILov + (screen_height + 1 - L) * tt.t_ILpf] +
532 N * [tt.t_ILnov + (screen_height + 1 - L) * tt.t_ILnpf]
534 ILov represents the basic insert line overhead. ILpf is the padding
535 required to allow the terminal time to move a line: insertion at line
536 L changes (screen_height + 1 - L) lines.
538 The first bracketed expression above is the overhead; the second is
539 the multiply factor. Both are dependent only on the position at
540 which the insert is performed. We store the overhead in
541 SCREEN_INSERT_COST (screen) and the multiply factor in
542 SCREEN_INSERTN_COST (screen). Note however that any insertion
543 must include at least one multiply factor. Rather than compute this
544 as SCREEN_INSERT_COST (screen)[line]+SCREEN_INSERTN_COST (screen)[line],
545 we add SCREEN_INSERTN_COST (screen) into SCREEN_INSERT_COST (screen).
546 This is reasonable because of the particular algorithm used in calcM.
548 Deletion is essentially the same as insertion.
551 do_line_insertion_deletion_costs (screen
,
552 ins_line_string
, multi_ins_string
,
553 del_line_string
, multi_del_string
,
554 setup_string
, cleanup_string
, coefficient
)
556 char *ins_line_string
, *multi_ins_string
;
557 char *del_line_string
, *multi_del_string
;
558 char *setup_string
, *cleanup_string
;
561 if (SCREEN_INSERT_COST (screen
) != 0)
563 SCREEN_INSERT_COST (screen
) =
564 (int *) xrealloc (SCREEN_INSERT_COST (screen
),
565 SCREEN_HEIGHT (screen
) * sizeof (int));
566 SCREEN_DELETEN_COST (screen
) =
567 (int *) xrealloc (SCREEN_DELETEN_COST (screen
),
568 SCREEN_HEIGHT (screen
) * sizeof (int));
569 SCREEN_INSERTN_COST (screen
) =
570 (int *) xrealloc (SCREEN_INSERTN_COST (screen
),
571 SCREEN_HEIGHT (screen
) * sizeof (int));
572 SCREEN_DELETE_COST (screen
) =
573 (int *) xrealloc (SCREEN_DELETE_COST (screen
),
574 SCREEN_HEIGHT (screen
) * sizeof (int));
578 SCREEN_INSERT_COST (screen
) =
579 (int *) xmalloc (SCREEN_HEIGHT (screen
) * sizeof (int));
580 SCREEN_DELETEN_COST (screen
) =
581 (int *) xmalloc (SCREEN_HEIGHT (screen
) * sizeof (int));
582 SCREEN_INSERTN_COST (screen
) =
583 (int *) xmalloc (SCREEN_HEIGHT (screen
) * sizeof (int));
584 SCREEN_DELETE_COST (screen
) =
585 (int *) xmalloc (SCREEN_HEIGHT (screen
) * sizeof (int));
588 ins_del_costs (screen
,
589 ins_line_string
, multi_ins_string
,
590 setup_string
, cleanup_string
,
591 SCREEN_INSERT_COST (screen
), SCREEN_INSERTN_COST (screen
),
593 ins_del_costs (screen
,
594 del_line_string
, multi_del_string
,
595 setup_string
, cleanup_string
,
596 SCREEN_DELETEN_COST (screen
), SCREEN_DELETE_COST (screen
),