1 /* Calculate what line insertion or deletion to do, and do it,
2 Copyright (C) 1985, 1986, 1990, 1993, 1994 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 frame, measured in characters.
34 Let's hope this is never more than 1000000 characters. */
36 #define INFINITY 1000000
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. */
51 unsigned char insertcount
;
52 /* Number of deletes so far in this run of deletes,
53 for the cost in deletecost. */
54 unsigned char deletecount
;
55 /* Number of writes so far since the last insert
56 or delete for the cost in writecost. */
57 unsigned char writecount
;
61 /* Determine, in matrix[i,j], the cost of updating the first j old
62 lines into the first i new lines using the general scrolling method.
63 This involves using insert or delete somewhere if i != j.
64 For each matrix elements, three kinds of costs are recorded:
65 the smallest cost that ends with an insert, the smallest
66 cost that ends with a delete, and the smallest cost that
67 ends with neither one. These are kept separate because
68 on some terminals the cost of doing an insert varies
69 depending on whether one was just done, etc. */
71 /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
72 old_hash[VPOS] is the hash code of the old line at VPOS.
73 new_hash[VPOS] is the hash code of the new line at VPOS.
74 Note that these are not true frame vpos's, but relative
75 to the place at which the first mismatch between old and
76 new contents appears. */
79 calculate_scrolling (frame
, matrix
, window_size
, lines_below
,
80 draw_cost
, old_hash
, new_hash
,
83 /* matrix is of size window_size + 1 on each side. */
84 struct matrix_elt
*matrix
;
92 int frame_height
= FRAME_HEIGHT (frame
);
93 register struct matrix_elt
*p
, *p1
;
94 register int cost
, cost1
;
96 int lines_moved
= window_size
+ (scroll_region_ok
? 0 : lines_below
);
97 /* first_insert_cost[I] is the cost of doing the first insert-line
98 at the I'th line of the lines we are considering,
99 where I is origin 1 (as it is below). */
100 int *first_insert_cost
101 = &FRAME_INSERT_COST (frame
)[frame_height
- 1 - lines_moved
];
102 int *first_delete_cost
103 = &FRAME_DELETE_COST (frame
)[frame_height
- 1 - lines_moved
];
104 int *next_insert_cost
105 = &FRAME_INSERTN_COST (frame
)[frame_height
- 1 - lines_moved
];
106 int *next_delete_cost
107 = &FRAME_DELETEN_COST (frame
)[frame_height
- 1 - lines_moved
];
109 /* Discourage long scrolls on fast lines.
110 Don't scroll nearly a full frame height unless it saves
111 at least 1/4 second. */
112 int extra_cost
= baud_rate
/ (10 * 4 * FRAME_HEIGHT (frame
));
117 /* initialize the top left corner of the matrix */
118 matrix
->writecost
= 0;
119 matrix
->insertcost
= INFINITY
;
120 matrix
->deletecost
= INFINITY
;
121 matrix
->insertcount
= 0;
122 matrix
->deletecount
= 0;
124 /* initialize the left edge of the matrix */
125 cost
= first_insert_cost
[1] - next_insert_cost
[1];
126 for (i
= 1; i
<= window_size
; i
++)
128 p
= matrix
+ i
* (window_size
+ 1);
129 cost
+= draw_cost
[i
] + next_insert_cost
[i
] + extra_cost
;
130 p
->insertcost
= cost
;
131 p
->writecost
= INFINITY
;
132 p
->deletecost
= INFINITY
;
137 /* initialize the top edge of the matrix */
138 cost
= first_delete_cost
[1] - next_delete_cost
[1];
139 for (j
= 1; j
<= window_size
; j
++)
141 cost
+= next_delete_cost
[j
];
142 matrix
[j
].deletecost
= cost
;
143 matrix
[j
].writecost
= INFINITY
;
144 matrix
[j
].insertcost
= INFINITY
;
145 matrix
[j
].deletecount
= j
;
146 matrix
[j
].insertcount
= 0;
149 /* `i' represents the vpos among new frame contents.
150 `j' represents the vpos among the old frame contents. */
151 p
= matrix
+ window_size
+ 2; /* matrix [1, 1] */
152 for (i
= 1; i
<= window_size
; i
++, p
++)
153 for (j
= 1; j
<= window_size
; j
++, p
++)
155 /* p contains the address of matrix [i, j] */
157 /* First calculate the cost assuming we do
158 not insert or delete above this line.
159 That is, if we update through line i-1
160 based on old lines through j-1,
161 and then just change old line j to new line i. */
162 p1
= p
- window_size
- 2; /* matrix [i-1, j-1] */
163 cost
= p1
->writecost
;
164 if (cost
> p1
->insertcost
)
165 cost
= p1
->insertcost
;
166 if (cost
> p1
->deletecost
)
167 cost
= p1
->deletecost
;
168 if (old_hash
[j
] != new_hash
[i
])
169 cost
+= draw_cost
[i
];
172 /* Calculate the cost if we do an insert-line
173 before outputting this line.
174 That is, we update through line i-1
175 based on old lines through j,
176 do an insert-line on line i,
177 and then output line i from scratch,
178 leaving old lines starting from j for reuse below. */
179 p1
= p
- window_size
- 1; /* matrix [i-1, j] */
180 /* No need to think about doing a delete followed
181 immediately by an insert. It cannot be as good
182 as not doing either of them. */
183 if (free_at_end
== i
)
185 cost
= p1
->writecost
;
186 cost1
= p1
->insertcost
;
190 cost
= p1
->writecost
+ first_insert_cost
[i
];
191 if ((int) p1
->insertcount
> i
)
193 cost1
= p1
->insertcost
+ next_insert_cost
[i
- p1
->insertcount
];
195 p
->insertcost
= min (cost
, cost1
) + draw_cost
[i
] + extra_cost
;
196 p
->insertcount
= (cost
< cost1
) ? 1 : p1
->insertcount
+ 1;
197 if ((int) p
->insertcount
> i
)
200 /* Calculate the cost if we do a delete line after
201 outputting this line.
202 That is, we update through line i
203 based on old lines through j-1,
204 and throw away old line j. */
205 p1
= p
- 1; /* matrix [i, j-1] */
206 /* No need to think about doing an insert followed
207 immediately by a delete. */
208 if (free_at_end
== i
)
210 cost
= p1
->writecost
;
211 cost1
= p1
->deletecost
;
215 cost
= p1
->writecost
+ first_delete_cost
[i
];
216 cost1
= p1
->deletecost
+ next_delete_cost
[i
];
218 p
->deletecost
= min (cost
, cost1
);
219 p
->deletecount
= (cost
< cost1
) ? 1 : p1
->deletecount
+ 1;
223 /* Perform insert-lines and delete-lines operations on FRAME according
224 to the costs in MATRIX, using the general scrolling method.
225 Update the frame's current_glyphs info to record what was done.
227 WINDOW_SIZE is the number of lines being considered for scrolling
228 and UNCHANGED_AT_TOP is the vpos of the first line being considered.
229 These two arguments can specify any contiguous range of lines.
231 We also shuffle the charstarts vectors for the lines
232 along with the glyphs; but the results are not quite right,
233 since we cannot offset them for changes in amount of text
234 in this line or that line. Luckily it doesn't matter,
235 since update_frame and update_line will copy in the proper
236 new charstarts vectors from the frame's desired_glyphs. */
239 do_scrolling (frame
, matrix
, window_size
, unchanged_at_top
)
241 struct matrix_elt
*matrix
;
243 int unchanged_at_top
;
245 register struct matrix_elt
*p
;
247 register struct frame_glyphs
*current_frame
;
248 /* temp_frame->enable[i] means line i has been moved to current_frame. */
249 register struct frame_glyphs
*temp_frame
;
250 struct queue
{ int count
, pos
; } *queue
;
251 int offset
= unchanged_at_top
;
257 queue
= (struct queue
*) alloca (FRAME_HEIGHT (frame
)
258 * sizeof (struct queue
));
260 current_frame
= FRAME_CURRENT_GLYPHS (frame
);
261 temp_frame
= FRAME_TEMP_GLYPHS (frame
);
263 bcopy (current_frame
->glyphs
, temp_frame
->glyphs
,
264 current_frame
->height
* sizeof (GLYPH
*));
265 bcopy (current_frame
->charstarts
, temp_frame
->charstarts
,
266 current_frame
->height
* sizeof (GLYPH
*));
267 bcopy (current_frame
->used
, temp_frame
->used
,
268 current_frame
->height
* sizeof (int));
269 bcopy (current_frame
->highlight
, temp_frame
->highlight
,
270 current_frame
->height
* sizeof (char));
271 bzero (temp_frame
->enable
, temp_frame
->height
* sizeof (char));
272 bcopy (current_frame
->bufp
, temp_frame
->bufp
,
273 current_frame
->height
* sizeof (int));
275 #ifdef HAVE_WINDOW_SYSTEM
276 if (FRAME_WINDOW_P (frame
))
278 bcopy (current_frame
->top_left_x
, temp_frame
->top_left_x
,
279 current_frame
->height
* sizeof (short));
280 bcopy (current_frame
->top_left_y
, temp_frame
->top_left_y
,
281 current_frame
->height
* sizeof (short));
282 bcopy (current_frame
->pix_width
, temp_frame
->pix_width
,
283 current_frame
->height
* sizeof (short));
284 bcopy (current_frame
->pix_height
, temp_frame
->pix_height
,
285 current_frame
->height
* sizeof (short));
286 bcopy (current_frame
->max_ascent
, temp_frame
->max_ascent
,
287 current_frame
->height
* sizeof (short));
293 while (i
> 0 || j
> 0)
295 p
= matrix
+ i
* (window_size
+ 1) + j
;
297 if (tem
< p
->writecost
&& tem
< p
->deletecost
)
299 /* Insert should be done at vpos i-1, plus maybe some before */
300 queue
[qi
].count
= p
->insertcount
;
302 queue
[qi
++].pos
= i
+ unchanged_at_top
;
304 else if (p
->deletecost
< p
->writecost
)
306 /* Old line at vpos j-1, and maybe some before it,
311 set_terminal_window (window_size
+ unchanged_at_top
);
314 ins_del_lines (j
+ unchanged_at_top
, - p
->deletecount
);
318 /* Best thing done here is no insert or delete */
319 /* Old line at vpos j-1 ends up at vpos i-1 */
320 current_frame
->glyphs
[i
+ offset
- 1]
321 = temp_frame
->glyphs
[j
+ offset
- 1];
322 current_frame
->charstarts
[i
+ offset
- 1]
323 = temp_frame
->charstarts
[j
+ offset
- 1];
324 current_frame
->used
[i
+ offset
- 1]
325 = temp_frame
->used
[j
+ offset
- 1];
326 current_frame
->highlight
[i
+ offset
- 1]
327 = temp_frame
->highlight
[j
+ offset
- 1];
329 temp_frame
->enable
[j
+ offset
- 1] = 1;
337 set_terminal_window (window_size
+ unchanged_at_top
);
341 /* Now do all insertions */
343 next
= unchanged_at_top
;
344 for (i
= qi
- 1; i
>= 0; i
--)
346 ins_del_lines (queue
[i
].pos
, queue
[i
].count
);
348 /* Mark the inserted lines as clear,
349 and put into them the line-contents strings
350 that were discarded during the deletions.
351 Those are the ones for which temp_frame->enable was not set. */
353 for (j
= tem
+ queue
[i
].count
- 1; j
>= tem
; j
--)
355 current_frame
->enable
[j
] = 0;
356 while (temp_frame
->enable
[next
])
358 current_frame
->glyphs
[j
] = temp_frame
->glyphs
[next
];
359 current_frame
->charstarts
[j
] = temp_frame
->charstarts
[next
++];
364 set_terminal_window (0);
367 /* Determine, in matrix[i,j], the cost of updating the first j
368 old lines into the first i new lines using the direct
369 scrolling method. When the old line and the new line have
370 different hash codes, the calculated cost of updating old
371 line j into new line i includes the cost of outputting new
372 line i, and if i != j, the cost of outputting the old line j
373 is also included, as a penalty for moving the line and then
374 erasing it. In addition, the cost of updating a sequence of
375 lines with constant i - j includes the cost of scrolling the
376 old lines into their new positions, unless i == j. Scrolling
377 is achieved by setting the screen window to avoid affecting
378 other lines below, and inserting or deleting lines at the top
379 of the scrolled region. The cost of scrolling a sequence of
380 lines includes the fixed cost of specifying a scroll region,
381 plus a variable cost which can depend upon the number of lines
382 involved and the distance by which they are scrolled, and an
383 extra cost to discourage long scrolls.
385 As reflected in the matrix, an insert or delete does not
386 correspond directly to the insertion or deletion which is
387 used in scrolling lines. An insert means that the value of i
388 has increased without a corresponding increase in the value
389 of j. A delete means that the value of j has increased
390 without a corresponding increase in the value of i. A write
391 means that i and j are both increased by the same amount, and
392 that the old lines will be moved to their new positions.
394 An insert following a delete is allowed only if i > j.
395 A delete following an insert is allowed only if i < j.
396 These restrictions ensure that the new lines in an insert
397 will always be blank as an effect of the neighboring writes.
398 Thus the calculated cost of an insert is simply the cost of
399 outputting the new line contents. The direct cost of a
400 delete is zero. Inserts and deletes indirectly affect the
401 total cost through their influence on subsequent writes. */
403 /* The vectors draw_cost, old_hash, and new_hash have the same
404 meanings here as in calculate_scrolling, and old_draw_cost
405 is the equivalent of draw_cost for the old line contents */
408 calculate_direct_scrolling (frame
, matrix
, window_size
, lines_below
,
409 draw_cost
, old_draw_cost
, old_hash
, new_hash
,
412 /* matrix is of size window_size + 1 on each side. */
413 struct matrix_elt
*matrix
;
422 int frame_height
= FRAME_HEIGHT (frame
);
423 register struct matrix_elt
*p
, *p1
;
424 register int cost
, cost1
, delta
;
426 /* first_insert_cost[-I] is the cost of doing the first insert-line
427 at a position I lines above the bottom line in the scroll window. */
428 int *first_insert_cost
429 = &FRAME_INSERT_COST (frame
)[frame_height
- 1];
430 int *first_delete_cost
431 = &FRAME_DELETE_COST (frame
)[frame_height
- 1];
432 int *next_insert_cost
433 = &FRAME_INSERTN_COST (frame
)[frame_height
- 1];
434 int *next_delete_cost
435 = &FRAME_DELETEN_COST (frame
)[frame_height
- 1];
439 /* Discourage long scrolls on fast lines.
440 Don't scroll nearly a full frame height unless it saves
441 at least 1/4 second. */
442 int extra_cost
= baud_rate
/ (10 * 4 * FRAME_HEIGHT (frame
));
447 /* Overhead of setting the scroll window, plus the extra cost
448 cost of scrolling by a distance of one. The extra cost is
449 added once for consistency with the cost vectors */
450 scroll_overhead
= scroll_region_cost
+ extra_cost
;
452 /* initialize the top left corner of the matrix */
453 matrix
->writecost
= 0;
454 matrix
->insertcost
= INFINITY
;
455 matrix
->deletecost
= INFINITY
;
456 matrix
->writecount
= 0;
457 matrix
->insertcount
= 0;
458 matrix
->deletecount
= 0;
460 /* initialize the left edge of the matrix */
462 for (i
= 1; i
<= window_size
; i
++)
464 p
= matrix
+ i
* (window_size
+ 1);
465 cost
+= draw_cost
[i
];
466 p
->insertcost
= cost
;
467 p
->writecost
= INFINITY
;
468 p
->deletecost
= INFINITY
;
474 /* initialize the top edge of the matrix */
475 for (j
= 1; j
<= window_size
; j
++)
477 matrix
[j
].deletecost
= 0;
478 matrix
[j
].writecost
= INFINITY
;
479 matrix
[j
].insertcost
= INFINITY
;
480 matrix
[j
].deletecount
= j
;
481 matrix
[j
].writecount
= 0;
482 matrix
[j
].insertcount
= 0;
485 /* `i' represents the vpos among new frame contents.
486 `j' represents the vpos among the old frame contents. */
487 p
= matrix
+ window_size
+ 2; /* matrix [1, 1] */
489 for (i
= 1; i
<= window_size
; i
++, p
++)
490 for (j
= 1; j
<= window_size
; j
++, p
++)
492 /* p contains the address of matrix [i, j] */
494 /* First calculate the cost assuming we do
495 not insert or delete above this line.
496 That is, if we update through line i-1
497 based on old lines through j-1,
498 and then just change old line j to new line i.
500 Depending on which choice gives the lower cost,
501 this usually involves either scrolling a single line
502 or extending a sequence of scrolled lines, but
503 when i == j, no scrolling is required. */
504 p1
= p
- window_size
- 2; /* matrix [i-1, j-1] */
505 cost
= p1
->insertcost
;
506 if (cost
> p1
->deletecost
)
507 cost
= p1
->deletecost
;
508 cost1
= p1
->writecost
;
514 p
->writecount
= p1
->writecount
+ 1;
518 if (old_hash
[j
] != new_hash
[i
])
520 cost
+= draw_cost
[i
];
529 /* The cost added here for scrolling the first line by
530 a distance N includes the overhead of setting the
531 scroll window, the cost of inserting N lines at a
532 position N lines above the bottom line of the window,
533 and an extra cost which is proportional to N. */
534 cost
+= scroll_overhead
+ first_insert_cost
[-delta
] +
535 (delta
-1) * (next_insert_cost
[-delta
] + extra_cost
);
537 /* In the most general case, the insertion overhead and
538 the multiply factor can grow linearly as the distance
539 from the bottom of the window increases. The incremental
540 cost of scrolling an additional line depends upon the
541 rate of change of these two parameters. Each of these
542 growth rates can be determined by a simple difference.
543 To reduce the cumulative effects of rounding error, we
544 vary the position at which the difference is computed. */
545 cost1
+= first_insert_cost
[-j
] - first_insert_cost
[1-j
] +
546 (delta
-1) * (next_insert_cost
[-j
] - next_insert_cost
[1-j
]);
551 cost
+= scroll_overhead
+ first_delete_cost
[-delta
] +
552 (delta
-1) * (next_delete_cost
[-delta
] + extra_cost
);
553 cost1
+= first_delete_cost
[-i
] - first_delete_cost
[1-i
] +
554 (delta
-1) * ( next_delete_cost
[-i
] - next_delete_cost
[1-i
]);
559 p
->writecount
= p1
->writecount
+ 1;
563 if (old_hash
[j
] != new_hash
[i
])
565 cost
+= draw_cost
[i
] + old_draw_cost
[j
];
570 /* Calculate the cost if we do an insert-line
571 before outputting this line.
572 That is, we update through line i-1
573 based on old lines through j,
574 do an insert-line on line i,
575 and then output line i from scratch,
576 leaving old lines starting from j for reuse below. */
577 p1
= p
- window_size
- 1; /* matrix [i-1, j] */
578 cost
= p1
->writecost
;
579 /* If i > j, an insert is allowed after a delete. */
580 if (i
> j
&& p1
->deletecost
< cost
)
581 cost
= p1
->deletecost
;
582 if (p1
->insertcost
<= cost
)
584 cost
= p1
->insertcost
;
585 p
->insertcount
= p1
->insertcount
+ 1;
589 cost
+= draw_cost
[i
];
590 p
->insertcost
= cost
;
592 /* Calculate the cost if we do a delete line after
593 outputting this line.
594 That is, we update through line i
595 based on old lines through j-1,
596 and throw away old line j. */
597 p1
= p
- 1; /* matrix [i, j-1] */
598 cost
= p1
->writecost
;
599 /* If i < j, a delete is allowed after an insert. */
600 if (i
< j
&& p1
->insertcost
< cost
)
601 cost
= p1
->insertcost
;
602 cost1
= p1
->deletecost
;
603 if (p1
->deletecost
<= cost
)
605 cost
= p1
->deletecost
;
606 p
->deletecount
= p1
->deletecount
+ 1;
610 p
->deletecost
= cost
;
614 /* Perform insert-lines and delete-lines operations on FRAME according
615 to the costs in MATRIX, using the direct scrolling method.
616 Update the frame's current_glyphs info to record what was done.
618 WINDOW_SIZE is the number of lines being considered for scrolling
619 and UNCHANGED_AT_TOP is the vpos of the first line being considered.
620 These two arguments can specify any contiguous range of lines.
622 We also shuffle the charstarts vectors for the lines
623 along with the glyphs; but the results are not quite right,
624 since we cannot offset them for changes in amount of text
625 in this line or that line. Luckily it doesn't matter,
626 since update_frame and update_line will copy in the proper
627 new charstarts vectors from the frame's desired_glyphs.
629 In the direct scrolling method, a new scroll window is selected
630 before each insertion or deletion, so that groups of lines can be
631 scrolled directly to their final vertical positions. This method
632 is described in more detail in calculate_direct_scrolling,
633 where the cost matrix for this approach is constructed. */
636 do_direct_scrolling (frame
, matrix
, window_size
, unchanged_at_top
)
638 struct matrix_elt
*matrix
;
640 int unchanged_at_top
;
642 register struct matrix_elt
*p
;
644 register struct frame_glyphs
*current_frame
;
645 /* temp_frame->enable[i] means line i has been moved to current_frame. */
646 register struct frame_glyphs
*temp_frame
;
647 struct alt_queue
{ int count
, pos
, window
; } *queue
;
648 int offset
= unchanged_at_top
;
654 /* A nonzero value of write_follows indicates that a write has been
655 selected, allowing either an insert or a delete to be selected next.
656 When write_follows is zero, a delete cannot be selected unless j < i,
657 and an insert cannot be selected unless i < j. This corresponds to
658 a similar restriction (with the ordering reversed) in
659 calculate_direct_scrolling, which is intended to ensure that lines
660 marked as inserted will be blank. */
661 int write_follows
= 1;
663 queue
= (struct alt_queue
*) alloca (FRAME_HEIGHT (frame
)
664 * sizeof (struct alt_queue
));
666 current_frame
= FRAME_CURRENT_GLYPHS (frame
);
667 temp_frame
= FRAME_TEMP_GLYPHS (frame
);
669 bcopy (current_frame
->glyphs
, temp_frame
->glyphs
,
670 current_frame
->height
* sizeof (GLYPH
*));
671 bcopy (current_frame
->charstarts
, temp_frame
->charstarts
,
672 current_frame
->height
* sizeof (GLYPH
*));
673 bcopy (current_frame
->used
, temp_frame
->used
,
674 current_frame
->height
* sizeof (int));
675 bcopy (current_frame
->highlight
, temp_frame
->highlight
,
676 current_frame
->height
* sizeof (char));
677 bzero (temp_frame
->enable
, temp_frame
->height
* sizeof (char));
678 bcopy (current_frame
->bufp
, temp_frame
->bufp
,
679 current_frame
->height
* sizeof (int));
681 #ifdef HAVE_WINDOW_SYSTEM
682 if (FRAME_WINDOW_P (frame
))
684 bcopy (current_frame
->top_left_x
, temp_frame
->top_left_x
,
685 current_frame
->height
* sizeof (short));
686 bcopy (current_frame
->top_left_y
, temp_frame
->top_left_y
,
687 current_frame
->height
* sizeof (short));
688 bcopy (current_frame
->pix_width
, temp_frame
->pix_width
,
689 current_frame
->height
* sizeof (short));
690 bcopy (current_frame
->pix_height
, temp_frame
->pix_height
,
691 current_frame
->height
* sizeof (short));
692 bcopy (current_frame
->max_ascent
, temp_frame
->max_ascent
,
693 current_frame
->height
* sizeof (short));
699 while (i
> 0 || j
> 0)
701 p
= matrix
+ i
* (window_size
+ 1) + j
;
703 if (tem
< p
->writecost
&& tem
< p
->deletecost
704 && (write_follows
|| i
< j
))
706 /* Insert should be done at vpos i-1, plus maybe some before.
707 Setting count to 0 in the queue marks this as an insert. */
709 queue
[qi
].window
= i
+ unchanged_at_top
;
712 queue
[qi
++].pos
= i
+ unchanged_at_top
;
714 else if (p
->deletecost
< p
->writecost
&& (write_follows
|| i
> j
))
716 /* Delete should be done at vpos j-1, plus maybe some before. */
722 /* One or more lines should be retained. */
727 /* Immediately scroll a group of lines downward */
728 set_terminal_window (i
+ unchanged_at_top
);
730 ins_del_lines (j
- tem
+ unchanged_at_top
, i
- j
);
734 /* Queue the upward scrolling of a group of lines */
735 queue
[qi
].pos
= i
- tem
+ unchanged_at_top
;
736 queue
[qi
].window
= j
+ unchanged_at_top
;
737 queue
[qi
++].count
= i
- j
;
740 /* Now copy the line-contents strings */
746 current_frame
->glyphs
[i
+ offset
]
747 = temp_frame
->glyphs
[j
+ offset
];
748 current_frame
->charstarts
[i
+ offset
]
749 = temp_frame
->charstarts
[j
+ offset
];
750 current_frame
->used
[i
+ offset
]
751 = temp_frame
->used
[j
+ offset
];
752 current_frame
->highlight
[i
+ offset
]
753 = temp_frame
->highlight
[j
+ offset
];
755 temp_frame
->enable
[j
+ offset
] = 1;
760 /* Now do all the upward scrolling, and copy the line-contents
761 strings for the blank lines of the recorded inserts. */
763 next
= unchanged_at_top
;
764 for (i
= qi
- 1; i
>= 0; i
--)
768 set_terminal_window (queue
[i
].window
);
770 ins_del_lines (queue
[i
].pos
, queue
[i
].count
);
774 /* Mark the inserted lines as clear,
775 and put into them the line-contents strings
776 that were discarded during the deletions.
777 Those are the ones for which temp_frame->enable was not set. */
779 for (j
= queue
[i
].window
- 1; j
>= tem
; j
--)
781 current_frame
->enable
[j
] = 0;
782 while (temp_frame
->enable
[next
])
784 current_frame
->glyphs
[j
] = temp_frame
->glyphs
[next
];
785 current_frame
->charstarts
[j
] = temp_frame
->charstarts
[next
++];
791 set_terminal_window (0);
795 scrolling_1 (frame
, window_size
, unchanged_at_top
, unchanged_at_bottom
,
796 draw_cost
, old_draw_cost
, old_hash
, new_hash
, free_at_end
)
798 int window_size
, unchanged_at_top
, unchanged_at_bottom
;
805 struct matrix_elt
*matrix
;
806 matrix
= ((struct matrix_elt
*)
807 alloca ((window_size
+ 1) * (window_size
+ 1) * sizeof *matrix
));
809 if (scroll_region_ok
)
811 calculate_direct_scrolling (frame
, matrix
, window_size
,
813 draw_cost
, old_draw_cost
,
814 old_hash
, new_hash
, free_at_end
);
815 do_direct_scrolling (frame
, matrix
, window_size
, unchanged_at_top
);
819 calculate_scrolling (frame
, matrix
, window_size
, unchanged_at_bottom
,
820 draw_cost
, old_hash
, new_hash
,
822 do_scrolling (frame
, matrix
, window_size
, unchanged_at_top
);
826 /* Return number of lines in common between current and desired frame contents
827 described to us only as vectors of hash codes OLDHASH and NEWHASH.
828 Consider only vpos range START to END (not including END).
829 Ignore short lines on the assumption that
830 avoiding redrawing such a line will have little weight. */
833 scrolling_max_lines_saved (start
, end
, oldhash
, newhash
, cost
)
835 int *oldhash
, *newhash
, *cost
;
837 struct { int hash
; int count
; } lines
[01000];
839 register int matchcount
= 0;
843 /* Compute a threshold which is 1/4 of average length of these lines. */
845 for (i
= start
; i
< end
; i
++)
846 avg_length
+= cost
[i
];
848 avg_length
/= end
- start
;
849 threshold
= avg_length
/ 4;
851 bzero (lines
, sizeof lines
);
853 /* Put new lines' hash codes in hash table.
854 Ignore lines shorter than the threshold.
855 Thus, if the lines that are in common
856 are mainly the ones that are short,
858 for (i
= start
; i
< end
; i
++)
860 if (cost
[i
] > threshold
)
862 h
= newhash
[i
] & 0777;
863 lines
[h
].hash
= newhash
[i
];
868 /* Look up old line hash codes in the hash table.
869 Count number of matches between old lines and new. */
871 for (i
= start
; i
< end
; i
++)
873 h
= oldhash
[i
] & 0777;
874 if (oldhash
[i
] == lines
[h
].hash
)
877 if (--lines
[h
].count
== 0)
885 /* Return a measure of the cost of moving the lines
886 starting with vpos FROM, up to but not including vpos TO,
887 down by AMOUNT lines (AMOUNT may be negative).
888 These are the same arguments that might be given to
889 scroll_frame_lines to perform this scrolling. */
891 scroll_cost (frame
, from
, to
, amount
)
893 int from
, to
, amount
;
895 /* Compute how many lines, at bottom of frame,
896 will not be involved in actual motion. */
899 int height
= FRAME_HEIGHT (frame
);
904 if (! scroll_region_ok
)
913 from
= temp
+ amount
;
917 offset
= height
- limit
;
920 (FRAME_INSERT_COST (frame
)[offset
+ from
]
921 + (amount
- 1) * FRAME_INSERTN_COST (frame
)[offset
+ from
]
922 + FRAME_DELETE_COST (frame
)[offset
+ to
]
923 + (amount
- 1) * FRAME_DELETEN_COST (frame
)[offset
+ to
]);
926 /* Calculate the line insertion/deletion
927 overhead and multiply factor values */
930 line_ins_del (frame
, ov1
, pf1
, ovn
, pfn
, ov
, mf
)
934 register int *ov
, *mf
;
937 register int frame_height
= FRAME_HEIGHT (frame
);
938 register int insert_overhead
= ov1
* 10;
939 register int next_insert_cost
= ovn
* 10;
941 for (i
= frame_height
-1; i
>= 0; i
--)
943 mf
[i
] = next_insert_cost
/ 10;
944 next_insert_cost
+= pfn
;
945 ov
[i
] = (insert_overhead
+ next_insert_cost
) / 10;
946 insert_overhead
+= pf1
;
951 ins_del_costs (frame
,
952 one_line_string
, multi_string
,
953 setup_string
, cleanup_string
,
954 costvec
, ncostvec
, coefficient
)
956 char *one_line_string
, *multi_string
;
957 char *setup_string
, *cleanup_string
;
958 int *costvec
, *ncostvec
;
963 string_cost (multi_string
) * coefficient
,
964 per_line_cost (multi_string
) * coefficient
,
965 0, 0, costvec
, ncostvec
);
966 else if (one_line_string
)
968 string_cost (setup_string
) + string_cost (cleanup_string
), 0,
969 string_cost (one_line_string
),
970 per_line_cost (one_line_string
),
978 /* Calculate the insert and delete line costs.
979 Note that this is done even when running with a window system
980 because we want to know how long scrolling takes (and avoid it).
981 This must be redone whenever the frame height changes.
983 We keep the ID costs in a precomputed array based on the position
984 at which the I or D is performed. Also, there are two kinds of ID
985 costs: the "once-only" and the "repeated". This is to handle both
986 those terminals that are able to insert N lines at a time (once-
987 only) and those that must repeatedly insert one line.
989 The cost to insert N lines at line L is
990 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
991 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
993 ILov represents the basic insert line overhead. ILpf is the padding
994 required to allow the terminal time to move a line: insertion at line
995 L changes (frame_height + 1 - L) lines.
997 The first bracketed expression above is the overhead; the second is
998 the multiply factor. Both are dependent only on the position at
999 which the insert is performed. We store the overhead in
1000 FRAME_INSERT_COST (frame) and the multiply factor in
1001 FRAME_INSERTN_COST (frame). Note however that any insertion
1002 must include at least one multiply factor. Rather than compute this
1003 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1004 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
1005 This is reasonable because of the particular algorithm used in calcM.
1007 Deletion is essentially the same as insertion.
1010 do_line_insertion_deletion_costs (frame
,
1011 ins_line_string
, multi_ins_string
,
1012 del_line_string
, multi_del_string
,
1013 setup_string
, cleanup_string
, coefficient
)
1015 char *ins_line_string
, *multi_ins_string
;
1016 char *del_line_string
, *multi_del_string
;
1017 char *setup_string
, *cleanup_string
;
1020 if (FRAME_INSERT_COST (frame
) != 0)
1022 FRAME_INSERT_COST (frame
) =
1023 (int *) xrealloc (FRAME_INSERT_COST (frame
),
1024 FRAME_HEIGHT (frame
) * sizeof (int));
1025 FRAME_DELETEN_COST (frame
) =
1026 (int *) xrealloc (FRAME_DELETEN_COST (frame
),
1027 FRAME_HEIGHT (frame
) * sizeof (int));
1028 FRAME_INSERTN_COST (frame
) =
1029 (int *) xrealloc (FRAME_INSERTN_COST (frame
),
1030 FRAME_HEIGHT (frame
) * sizeof (int));
1031 FRAME_DELETE_COST (frame
) =
1032 (int *) xrealloc (FRAME_DELETE_COST (frame
),
1033 FRAME_HEIGHT (frame
) * sizeof (int));
1037 FRAME_INSERT_COST (frame
) =
1038 (int *) xmalloc (FRAME_HEIGHT (frame
) * sizeof (int));
1039 FRAME_DELETEN_COST (frame
) =
1040 (int *) xmalloc (FRAME_HEIGHT (frame
) * sizeof (int));
1041 FRAME_INSERTN_COST (frame
) =
1042 (int *) xmalloc (FRAME_HEIGHT (frame
) * sizeof (int));
1043 FRAME_DELETE_COST (frame
) =
1044 (int *) xmalloc (FRAME_HEIGHT (frame
) * sizeof (int));
1047 ins_del_costs (frame
,
1048 ins_line_string
, multi_ins_string
,
1049 setup_string
, cleanup_string
,
1050 FRAME_INSERT_COST (frame
), FRAME_INSERTN_COST (frame
),
1052 ins_del_costs (frame
,
1053 del_line_string
, multi_del_string
,
1054 setup_string
, cleanup_string
,
1055 FRAME_DELETE_COST (frame
), FRAME_DELETEN_COST (frame
),