(top level comment): Updated to reflect the fact that
[emacs.git] / src / scroll.c
blob83e029f417cfe3f7a43729c5ae85ebcc03835dec
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)
9 any later version.
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, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
22 #include <config.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "termchar.h"
26 #include "lisp.h"
27 #include "dispextern.h"
28 #include "keyboard.h"
29 #include "frame.h"
30 #include "window.h"
32 #define max(a, b) ((a) > (b) ? (a) : (b))
33 #define min(a, b) ((a) < (b) ? (a) : (b))
35 /* All costs measured in characters.
36 So no cost can exceed the area of a frame, measured in characters.
37 Let's hope this is never more than 1000000 characters. */
39 #define INFINITY 1000000
41 struct matrix_elt
43 /* Cost of outputting through this line
44 if no insert/delete is done just above it. */
45 int writecost;
46 /* Cost of outputting through this line
47 if an insert is done just above it. */
48 int insertcost;
49 /* Cost of outputting through this line
50 if a delete is done just above it. */
51 int deletecost;
52 /* Number of inserts so far in this run of inserts,
53 for the cost in insertcost. */
54 unsigned char insertcount;
55 /* Number of deletes so far in this run of deletes,
56 for the cost in deletecost. */
57 unsigned char deletecount;
58 /* Number of writes so far since the last insert
59 or delete for the cost in writecost. */
60 unsigned char writecount;
63 static void do_direct_scrolling P_ ((struct glyph_matrix *,
64 struct matrix_elt *,
65 int, int));
66 static void do_scrolling P_ ((struct glyph_matrix *,
67 struct matrix_elt *,
68 int, int));
71 /* Determine, in matrix[i,j], the cost of updating the first j old
72 lines into the first i new lines using the general scrolling method.
73 This involves using insert or delete somewhere if i != j.
74 For each matrix elements, three kinds of costs are recorded:
75 the smallest cost that ends with an insert, the smallest
76 cost that ends with a delete, and the smallest cost that
77 ends with neither one. These are kept separate because
78 on some terminals the cost of doing an insert varies
79 depending on whether one was just done, etc. */
81 /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
82 old_hash[VPOS] is the hash code of the old line at VPOS.
83 new_hash[VPOS] is the hash code of the new line at VPOS.
84 Note that these are not true frame vpos's, but relative
85 to the place at which the first mismatch between old and
86 new contents appears. */
88 static void
89 calculate_scrolling (frame, matrix, window_size, lines_below,
90 draw_cost, old_hash, new_hash,
91 free_at_end)
92 FRAME_PTR frame;
93 /* matrix is of size window_size + 1 on each side. */
94 struct matrix_elt *matrix;
95 int window_size;
96 int *draw_cost;
97 int *old_hash;
98 int *new_hash;
99 int free_at_end;
101 register int i, j;
102 int frame_height = FRAME_HEIGHT (frame);
103 register struct matrix_elt *p, *p1;
104 register int cost, cost1;
106 int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below);
107 /* first_insert_cost[I] is the cost of doing the first insert-line
108 at the i'th line of the lines we are considering,
109 where I is origin 1 (as it is below). */
110 int *first_insert_cost
111 = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved];
112 int *first_delete_cost
113 = &FRAME_DELETE_COST (frame)[frame_height - 1 - lines_moved];
114 int *next_insert_cost
115 = &FRAME_INSERTN_COST (frame)[frame_height - 1 - lines_moved];
116 int *next_delete_cost
117 = &FRAME_DELETEN_COST (frame)[frame_height - 1 - lines_moved];
119 /* Discourage long scrolls on fast lines.
120 Don't scroll nearly a full frame height unless it saves
121 at least 1/4 second. */
122 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
124 if (baud_rate <= 0)
125 extra_cost = 1;
127 /* initialize the top left corner of the matrix */
128 matrix->writecost = 0;
129 matrix->insertcost = INFINITY;
130 matrix->deletecost = INFINITY;
131 matrix->insertcount = 0;
132 matrix->deletecount = 0;
134 /* initialize the left edge of the matrix */
135 cost = first_insert_cost[1] - next_insert_cost[1];
136 for (i = 1; i <= window_size; i++)
138 p = matrix + i * (window_size + 1);
139 cost += draw_cost[i] + next_insert_cost[i] + extra_cost;
140 p->insertcost = cost;
141 p->writecost = INFINITY;
142 p->deletecost = INFINITY;
143 p->insertcount = i;
144 p->deletecount = 0;
147 /* initialize the top edge of the matrix */
148 cost = first_delete_cost[1] - next_delete_cost[1];
149 for (j = 1; j <= window_size; j++)
151 cost += next_delete_cost[j];
152 matrix[j].deletecost = cost;
153 matrix[j].writecost = INFINITY;
154 matrix[j].insertcost = INFINITY;
155 matrix[j].deletecount = j;
156 matrix[j].insertcount = 0;
159 /* `i' represents the vpos among new frame contents.
160 `j' represents the vpos among the old frame contents. */
161 p = matrix + window_size + 2; /* matrix [1, 1] */
162 for (i = 1; i <= window_size; i++, p++)
163 for (j = 1; j <= window_size; j++, p++)
165 /* p contains the address of matrix [i, j] */
167 /* First calculate the cost assuming we do
168 not insert or delete above this line.
169 That is, if we update through line i-1
170 based on old lines through j-1,
171 and then just change old line j to new line i. */
172 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
173 cost = p1->writecost;
174 if (cost > p1->insertcost)
175 cost = p1->insertcost;
176 if (cost > p1->deletecost)
177 cost = p1->deletecost;
178 if (old_hash[j] != new_hash[i])
179 cost += draw_cost[i];
180 p->writecost = cost;
182 /* Calculate the cost if we do an insert-line
183 before outputting this line.
184 That is, we update through line i-1
185 based on old lines through j,
186 do an insert-line on line i,
187 and then output line i from scratch,
188 leaving old lines starting from j for reuse below. */
189 p1 = p - window_size - 1; /* matrix [i-1, j] */
190 /* No need to think about doing a delete followed
191 immediately by an insert. It cannot be as good
192 as not doing either of them. */
193 if (free_at_end == i)
195 cost = p1->writecost;
196 cost1 = p1->insertcost;
198 else
200 cost = p1->writecost + first_insert_cost[i];
201 if ((int) p1->insertcount > i)
202 abort ();
203 cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
205 p->insertcost = min (cost, cost1) + draw_cost[i] + extra_cost;
206 p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
207 if ((int) p->insertcount > i)
208 abort ();
210 /* Calculate the cost if we do a delete line after
211 outputting this line.
212 That is, we update through line i
213 based on old lines through j-1,
214 and throw away old line j. */
215 p1 = p - 1; /* matrix [i, j-1] */
216 /* No need to think about doing an insert followed
217 immediately by a delete. */
218 if (free_at_end == i)
220 cost = p1->writecost;
221 cost1 = p1->deletecost;
223 else
225 cost = p1->writecost + first_delete_cost[i];
226 cost1 = p1->deletecost + next_delete_cost[i];
228 p->deletecost = min (cost, cost1);
229 p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
235 /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
236 according to the costs in MATRIX, using the general scrolling
237 method that is used if the terminal does not support the setting of
238 scroll windows (scroll_region_ok == 0).
240 WINDOW_SIZE is the number of lines being considered for scrolling
241 and UNCHANGED_AT_TOP is the vpos of the first line being
242 considered. These two arguments can specify any contiguous range
243 of lines. */
245 static void
246 do_scrolling (current_matrix, matrix, window_size, unchanged_at_top)
247 struct glyph_matrix *current_matrix;
248 struct matrix_elt *matrix;
249 int window_size;
250 int unchanged_at_top;
252 struct matrix_elt *p;
253 int i, j, k;
255 /* Set to 1 if we have set a terminal window with
256 set_terminal_window. */
257 int terminal_window_p = 0;
259 /* A queue for line insertions to be done. */
260 struct queue { int count, pos; };
261 struct queue *queue_start
262 = (struct queue *) alloca (current_matrix->nrows * sizeof (struct queue));
263 struct queue *queue = queue_start;
265 char *retained_p = (char *) alloca (window_size * sizeof (char));
266 int *copy_from = (int *) alloca (window_size * sizeof (int));
268 /* Zero means line is empty. */
269 bzero (retained_p, window_size * sizeof (char));
270 for (k = 0; k < window_size; ++k)
271 copy_from[k] = -1;
273 #define CHECK_BOUNDS \
274 do \
276 int k; \
277 for (k = 0; k < window_size; ++k) \
278 xassert (copy_from[k] == -1 \
279 || (copy_from[k] >= 0 && copy_from[k] < window_size)); \
281 while (0);
283 /* When j is advanced, this corresponds to deleted lines.
284 When i is advanced, this corresponds to inserted lines. */
285 i = j = window_size;
286 while (i > 0 || j > 0)
288 p = matrix + i * (window_size + 1) + j;
290 if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
292 /* Insert should be done at vpos i-1, plus maybe some before.
293 Queue the screen operation to be performed. */
294 queue->count = p->insertcount;
295 queue->pos = i + unchanged_at_top - p->insertcount;
296 ++queue;
298 /* By incrementing I, we leave room in the result rows
299 for the empty rows opened up. */
300 i -= p->insertcount;
302 else if (p->deletecost < p->writecost)
304 /* Old line at vpos j-1, and maybe some before it, should be
305 deleted. By decrementing J, we skip some lines in the
306 temp_rows which is equivalent to omitting these lines in
307 the result rows, thus deleting them. */
308 j -= p->deletecount;
310 /* Set the terminal window, if not done already. */
311 if (! terminal_window_p)
313 set_terminal_window (window_size + unchanged_at_top);
314 terminal_window_p = 1;
317 /* Delete lines on the terminal. */
318 ins_del_lines (j + unchanged_at_top, - p->deletecount);
320 else
322 /* Best thing done here is no insert or delete, i.e. a write. */
323 --i, --j;
324 xassert (i >= 0 && i < window_size);
325 xassert (j >= 0 && j < window_size);
326 copy_from[i] = j;
327 retained_p[j] = 1;
329 #if GLYPH_DEBUG
330 CHECK_BOUNDS;
331 #endif
335 /* Now do all insertions queued above. */
336 if (queue > queue_start)
338 int next = -1;
340 /* Set the terminal window if not yet done. */
341 if (!terminal_window_p)
343 set_terminal_window (window_size + unchanged_at_top);
344 terminal_window_p = 1;
349 --queue;
351 /* Do the deletion on the terminal. */
352 ins_del_lines (queue->pos, queue->count);
354 /* All lines in the range deleted become empty in the glyph
355 matrix. Assign to them glyph rows that are not retained.
356 K is the starting position of the deleted range relative
357 to the window we are working in. */
358 k = queue->pos - unchanged_at_top;
359 for (j = 0; j < queue->count; ++j)
361 /* Find the next row not retained. */
362 while (retained_p[++next])
365 /* Record that this row is to be used for the empty
366 glyph row j. */
367 copy_from[k + j] = next;
370 while (queue > queue_start);
374 for (k = 0; k < window_size; ++k)
375 xassert (copy_from[k] >= 0 && copy_from[k] < window_size);
377 /* Perform the row swizzling. */
378 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
379 copy_from, retained_p);
381 /* Some sanity checks if GLYPH_DEBUG != 0. */
382 CHECK_MATRIX (current_matrix);
384 if (terminal_window_p)
385 set_terminal_window (0);
389 /* Determine, in matrix[i,j], the cost of updating the first j
390 old lines into the first i new lines using the direct
391 scrolling method. When the old line and the new line have
392 different hash codes, the calculated cost of updating old
393 line j into new line i includes the cost of outputting new
394 line i, and if i != j, the cost of outputting the old line j
395 is also included, as a penalty for moving the line and then
396 erasing it. In addition, the cost of updating a sequence of
397 lines with constant i - j includes the cost of scrolling the
398 old lines into their new positions, unless i == j. Scrolling
399 is achieved by setting the screen window to avoid affecting
400 other lines below, and inserting or deleting lines at the top
401 of the scrolled region. The cost of scrolling a sequence of
402 lines includes the fixed cost of specifying a scroll region,
403 plus a variable cost which can depend upon the number of lines
404 involved and the distance by which they are scrolled, and an
405 extra cost to discourage long scrolls.
407 As reflected in the matrix, an insert or delete does not
408 correspond directly to the insertion or deletion which is
409 used in scrolling lines. An insert means that the value of i
410 has increased without a corresponding increase in the value
411 of j. A delete means that the value of j has increased
412 without a corresponding increase in the value of i. A write
413 means that i and j are both increased by the same amount, and
414 that the old lines will be moved to their new positions.
416 An insert following a delete is allowed only if i > j.
417 A delete following an insert is allowed only if i < j.
418 These restrictions ensure that the new lines in an insert
419 will always be blank as an effect of the neighboring writes.
420 Thus the calculated cost of an insert is simply the cost of
421 outputting the new line contents. The direct cost of a
422 delete is zero. Inserts and deletes indirectly affect the
423 total cost through their influence on subsequent writes. */
425 /* The vectors draw_cost, old_hash, and new_hash have the same
426 meanings here as in calculate_scrolling, and old_draw_cost
427 is the equivalent of draw_cost for the old line contents */
429 static void
430 calculate_direct_scrolling (frame, matrix, window_size, lines_below,
431 draw_cost, old_draw_cost, old_hash, new_hash,
432 free_at_end)
433 FRAME_PTR frame;
434 /* matrix is of size window_size + 1 on each side. */
435 struct matrix_elt *matrix;
436 int window_size;
437 int *draw_cost;
438 int *old_draw_cost;
439 int *old_hash;
440 int *new_hash;
441 int free_at_end;
443 register int i, j;
444 int frame_height = FRAME_HEIGHT (frame);
445 register struct matrix_elt *p, *p1;
446 register int cost, cost1, delta;
448 /* first_insert_cost[-I] is the cost of doing the first insert-line
449 at a position I lines above the bottom line in the scroll window. */
450 int *first_insert_cost
451 = &FRAME_INSERT_COST (frame)[frame_height - 1];
452 int *first_delete_cost
453 = &FRAME_DELETE_COST (frame)[frame_height - 1];
454 int *next_insert_cost
455 = &FRAME_INSERTN_COST (frame)[frame_height - 1];
456 int *next_delete_cost
457 = &FRAME_DELETEN_COST (frame)[frame_height - 1];
459 int scroll_overhead;
461 /* Discourage long scrolls on fast lines.
462 Don't scroll nearly a full frame height unless it saves
463 at least 1/4 second. */
464 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
466 if (baud_rate <= 0)
467 extra_cost = 1;
469 /* Overhead of setting the scroll window, plus the extra cost
470 cost of scrolling by a distance of one. The extra cost is
471 added once for consistency with the cost vectors */
472 scroll_overhead = scroll_region_cost + extra_cost;
474 /* initialize the top left corner of the matrix */
475 matrix->writecost = 0;
476 matrix->insertcost = INFINITY;
477 matrix->deletecost = INFINITY;
478 matrix->writecount = 0;
479 matrix->insertcount = 0;
480 matrix->deletecount = 0;
482 /* initialize the left edge of the matrix */
483 cost = 0;
484 for (i = 1; i <= window_size; i++)
486 p = matrix + i * (window_size + 1);
487 cost += draw_cost[i];
488 p->insertcost = cost;
489 p->writecost = INFINITY;
490 p->deletecost = INFINITY;
491 p->insertcount = i;
492 p->writecount = 0;
493 p->deletecount = 0;
496 /* initialize the top edge of the matrix */
497 for (j = 1; j <= window_size; j++)
499 matrix[j].deletecost = 0;
500 matrix[j].writecost = INFINITY;
501 matrix[j].insertcost = INFINITY;
502 matrix[j].deletecount = j;
503 matrix[j].writecount = 0;
504 matrix[j].insertcount = 0;
507 /* `i' represents the vpos among new frame contents.
508 `j' represents the vpos among the old frame contents. */
509 p = matrix + window_size + 2; /* matrix [1, 1] */
511 for (i = 1; i <= window_size; i++, p++)
512 for (j = 1; j <= window_size; j++, p++)
514 /* p contains the address of matrix [i, j] */
516 /* First calculate the cost assuming we do
517 not insert or delete above this line.
518 That is, if we update through line i-1
519 based on old lines through j-1,
520 and then just change old line j to new line i.
522 Depending on which choice gives the lower cost,
523 this usually involves either scrolling a single line
524 or extending a sequence of scrolled lines, but
525 when i == j, no scrolling is required. */
526 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
527 cost = p1->insertcost;
528 if (cost > p1->deletecost)
529 cost = p1->deletecost;
530 cost1 = p1->writecost;
531 if (i == j)
533 if (cost > cost1)
535 cost = cost1;
536 p->writecount = p1->writecount + 1;
538 else
539 p->writecount = 1;
540 if (old_hash[j] != new_hash[i])
542 cost += draw_cost[i];
545 else
547 if (i > j)
549 delta = i - j;
551 /* The cost added here for scrolling the first line by
552 a distance N includes the overhead of setting the
553 scroll window, the cost of inserting N lines at a
554 position N lines above the bottom line of the window,
555 and an extra cost which is proportional to N. */
556 cost += scroll_overhead + first_insert_cost[-delta] +
557 (delta-1) * (next_insert_cost[-delta] + extra_cost);
559 /* In the most general case, the insertion overhead and
560 the multiply factor can grow linearly as the distance
561 from the bottom of the window increases. The incremental
562 cost of scrolling an additional line depends upon the
563 rate of change of these two parameters. Each of these
564 growth rates can be determined by a simple difference.
565 To reduce the cumulative effects of rounding error, we
566 vary the position at which the difference is computed. */
567 cost1 += first_insert_cost[-j] - first_insert_cost[1-j] +
568 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
570 else
572 delta = j - i;
573 cost += scroll_overhead + first_delete_cost[-delta] +
574 (delta-1) * (next_delete_cost[-delta] + extra_cost);
575 cost1 += first_delete_cost[-i] - first_delete_cost[1-i] +
576 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
578 if (cost1 < cost)
580 cost = cost1;
581 p->writecount = p1->writecount + 1;
583 else
584 p->writecount = 1;
585 if (old_hash[j] != new_hash[i])
587 cost += draw_cost[i] + old_draw_cost[j];
590 p->writecost = cost;
592 /* Calculate the cost if we do an insert-line
593 before outputting this line.
594 That is, we update through line i-1
595 based on old lines through j,
596 do an insert-line on line i,
597 and then output line i from scratch,
598 leaving old lines starting from j for reuse below. */
599 p1 = p - window_size - 1; /* matrix [i-1, j] */
600 cost = p1->writecost;
601 /* If i > j, an insert is allowed after a delete. */
602 if (i > j && p1->deletecost < cost)
603 cost = p1->deletecost;
604 if (p1->insertcost <= cost)
606 cost = p1->insertcost;
607 p->insertcount = p1->insertcount + 1;
609 else
610 p->insertcount = 1;
611 cost += draw_cost[i];
612 p->insertcost = cost;
614 /* Calculate the cost if we do a delete line after
615 outputting this line.
616 That is, we update through line i
617 based on old lines through j-1,
618 and throw away old line j. */
619 p1 = p - 1; /* matrix [i, j-1] */
620 cost = p1->writecost;
621 /* If i < j, a delete is allowed after an insert. */
622 if (i < j && p1->insertcost < cost)
623 cost = p1->insertcost;
624 cost1 = p1->deletecost;
625 if (p1->deletecost <= cost)
627 cost = p1->deletecost;
628 p->deletecount = p1->deletecount + 1;
630 else
631 p->deletecount = 1;
632 p->deletecost = cost;
638 /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
639 according to the costs in MATRIX, using the direct scrolling method
640 which is used when the terminal supports setting a scroll window
641 (scroll_region_ok).
643 WINDOW_SIZE is the number of lines being considered for scrolling
644 and UNCHANGED_AT_TOP is the vpos of the first line being
645 considered. These two arguments can specify any contiguous range
646 of lines.
648 In the direct scrolling method, a new scroll window is selected
649 before each insertion or deletion, so that groups of lines can be
650 scrolled directly to their final vertical positions. This method
651 is described in more detail in calculate_direct_scrolling, where
652 the cost matrix for this approach is constructed. */
654 static void
655 do_direct_scrolling (current_matrix, cost_matrix, window_size,
656 unchanged_at_top)
657 struct glyph_matrix *current_matrix;
658 struct matrix_elt *cost_matrix;
659 int window_size;
660 int unchanged_at_top;
662 struct matrix_elt *p;
663 int i, j;
665 /* A queue of deletions and insertions to be performed. */
666 struct alt_queue { int count, pos, window; };
667 struct alt_queue *queue_start = (struct alt_queue *)
668 alloca (window_size * sizeof *queue_start);
669 struct alt_queue *queue = queue_start;
671 /* Set to 1 if a terminal window has been set with
672 set_terminal_window: */
673 int terminal_window_p = 0;
675 /* A nonzero value of write_follows indicates that a write has been
676 selected, allowing either an insert or a delete to be selected
677 next. When write_follows is zero, a delete cannot be selected
678 unless j < i, and an insert cannot be selected unless i < j.
679 This corresponds to a similar restriction (with the ordering
680 reversed) in calculate_direct_scrolling, which is intended to
681 ensure that lines marked as inserted will be blank. */
682 int write_follows_p = 1;
684 /* For each row in the new matrix what row of the old matrix it is. */
685 int *copy_from = (int *) alloca (window_size * sizeof (int));
687 /* Non-zero for each row in the new matrix that is retained from the
688 old matrix. Lines not retained are empty. */
689 char *retained_p = (char *) alloca (window_size * sizeof (char));
691 bzero (retained_p, window_size * sizeof (char));
693 /* Perform some sanity checks when GLYPH_DEBUG is on. */
694 CHECK_MATRIX (current_matrix);
696 /* We are working on the line range UNCHANGED_AT_TOP ...
697 UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX.
698 We step through lines in this range from the end to the start. I
699 is an index into new lines, j an index into old lines. The cost
700 matrix determines what to do for ranges of indices.
702 If i is decremented without also decrementing j, this corresponds
703 to inserting empty lines in the result. If j is decremented
704 without also decrementing i, this corresponds to omitting these
705 lines in the new rows, i.e. rows are deleted. */
706 i = j = window_size;
708 while (i > 0 || j > 0)
710 p = cost_matrix + i * (window_size + 1) + j;
712 if (p->insertcost < p->writecost
713 && p->insertcost < p->deletecost
714 && (write_follows_p || i < j))
716 /* Insert is cheaper than deleting or writing lines. Leave
717 a hole in the result display that will be filled with
718 empty lines when the queue is emptied. */
719 queue->count = 0;
720 queue->window = i;
721 queue->pos = i - p->insertcount;
722 ++queue;
724 i -= p->insertcount;
725 write_follows_p = 0;
727 else if (p->deletecost < p->writecost
728 && (write_follows_p || i > j))
730 /* Deleting lines is cheaper. By decrementing J, omit
731 deletecount lines from the original. */
732 write_follows_p = 0;
733 j -= p->deletecount;
735 else
737 /* One or more lines should be written. In the direct
738 scrolling method we do this by scrolling the lines to the
739 place they belong. */
740 int n_to_write = p->writecount;
741 write_follows_p = 1;
742 xassert (n_to_write > 0);
744 if (i > j)
746 /* Immediately insert lines */
747 set_terminal_window (i + unchanged_at_top);
748 terminal_window_p = 1;
749 ins_del_lines (j - n_to_write + unchanged_at_top, i - j);
751 else if (i < j)
753 /* Queue the deletion of a group of lines */
754 queue->pos = i - n_to_write + unchanged_at_top;
755 queue->window = j + unchanged_at_top;
756 queue->count = i - j;
757 ++queue;
760 while (n_to_write > 0)
762 --i, --j, --n_to_write;
763 copy_from[i] = j;
764 retained_p[j] = 1;
769 /* Do queued operations. */
770 if (queue > queue_start)
772 int next = -1;
776 --queue;
777 if (queue->count)
779 set_terminal_window (queue->window);
780 terminal_window_p = 1;
781 ins_del_lines (queue->pos, queue->count);
783 else
785 for (j = queue->window - 1; j >= queue->pos; --j)
787 while (retained_p[++next])
789 copy_from[j] = next;
793 while (queue > queue_start);
796 /* Now, for each row I in the range of rows we are working on,
797 copy_from[i] gives the original line to copy to I, and
798 retained_p[copy_from[i]] is zero if line I in the new display is
799 empty. */
800 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
801 copy_from, retained_p);
803 if (terminal_window_p)
804 set_terminal_window (0);
809 void
810 scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
811 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
812 FRAME_PTR frame;
813 int window_size, unchanged_at_top, unchanged_at_bottom;
814 int *draw_cost;
815 int *old_draw_cost;
816 int *old_hash;
817 int *new_hash;
818 int free_at_end;
820 struct matrix_elt *matrix;
821 matrix = ((struct matrix_elt *)
822 alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
824 if (scroll_region_ok)
826 calculate_direct_scrolling (frame, matrix, window_size,
827 unchanged_at_bottom,
828 draw_cost, old_draw_cost,
829 old_hash, new_hash, free_at_end);
830 do_direct_scrolling (frame->current_matrix,
831 matrix, window_size, unchanged_at_top);
833 else
835 calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
836 draw_cost, old_hash, new_hash,
837 free_at_end);
838 do_scrolling (frame->current_matrix, matrix, window_size,
839 unchanged_at_top);
845 /* Return number of lines in common between current and desired frame
846 contents described to us only as vectors of hash codes OLDHASH and
847 NEWHASH. Consider only vpos range START to END (not including
848 END). Ignore short lines on the assumption that avoiding redrawing
849 such a line will have little weight. */
852 scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
853 int start, end;
854 int *oldhash, *newhash, *cost;
856 struct { int hash; int count; } lines[01000];
857 register int i, h;
858 register int matchcount = 0;
859 int avg_length = 0;
860 int threshold;
862 /* Compute a threshold which is 1/4 of average length of these lines. */
864 for (i = start; i < end; i++)
865 avg_length += cost[i];
867 avg_length /= end - start;
868 threshold = avg_length / 4;
870 bzero (lines, sizeof lines);
872 /* Put new lines' hash codes in hash table. Ignore lines shorter
873 than the threshold. Thus, if the lines that are in common are
874 mainly the ones that are short, they won't count. */
875 for (i = start; i < end; i++)
877 if (cost[i] > threshold)
879 h = newhash[i] & 0777;
880 lines[h].hash = newhash[i];
881 lines[h].count++;
885 /* Look up old line hash codes in the hash table. Count number of
886 matches between old lines and new. */
887 for (i = start; i < end; i++)
889 h = oldhash[i] & 0777;
890 if (oldhash[i] == lines[h].hash)
892 matchcount++;
893 if (--lines[h].count == 0)
894 lines[h].hash = 0;
898 return matchcount;
901 /* Return a measure of the cost of moving the lines starting with vpos
902 FROM, up to but not including vpos TO, down by AMOUNT lines (AMOUNT
903 may be negative). These are the same arguments that might be given
904 to scroll_frame_lines to perform this scrolling. */
907 scroll_cost (frame, from, to, amount)
908 FRAME_PTR frame;
909 int from, to, amount;
911 /* Compute how many lines, at bottom of frame,
912 will not be involved in actual motion. */
913 int limit = to;
914 int offset;
915 int height = FRAME_HEIGHT (frame);
917 if (amount == 0)
918 return 0;
920 if (! scroll_region_ok)
921 limit = height;
922 else if (amount > 0)
923 limit += amount;
925 if (amount < 0)
927 int temp = to;
928 to = from + amount;
929 from = temp + amount;
930 amount = - amount;
933 offset = height - limit;
935 return
936 (FRAME_INSERT_COST (frame)[offset + from]
937 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
938 + FRAME_DELETE_COST (frame)[offset + to]
939 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
942 /* Calculate the line insertion/deletion
943 overhead and multiply factor values */
945 static void
946 line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
947 FRAME_PTR frame;
948 int ov1, ovn;
949 int pf1, pfn;
950 register int *ov, *mf;
952 register int i;
953 register int frame_height = FRAME_HEIGHT (frame);
954 register int insert_overhead = ov1 * 10;
955 register int next_insert_cost = ovn * 10;
957 for (i = frame_height-1; i >= 0; i--)
959 mf[i] = next_insert_cost / 10;
960 next_insert_cost += pfn;
961 ov[i] = (insert_overhead + next_insert_cost) / 10;
962 insert_overhead += pf1;
966 static void
967 ins_del_costs (frame,
968 one_line_string, multi_string,
969 setup_string, cleanup_string,
970 costvec, ncostvec, coefficient)
971 FRAME_PTR frame;
972 char *one_line_string, *multi_string;
973 char *setup_string, *cleanup_string;
974 int *costvec, *ncostvec;
975 int coefficient;
977 if (multi_string)
978 line_ins_del (frame,
979 string_cost (multi_string) * coefficient,
980 per_line_cost (multi_string) * coefficient,
981 0, 0, costvec, ncostvec);
982 else if (one_line_string)
983 line_ins_del (frame,
984 string_cost (setup_string) + string_cost (cleanup_string), 0,
985 string_cost (one_line_string),
986 per_line_cost (one_line_string),
987 costvec, ncostvec);
988 else
989 line_ins_del (frame,
990 9999, 0, 9999, 0,
991 costvec, ncostvec);
994 /* Calculate the insert and delete line costs.
995 Note that this is done even when running with a window system
996 because we want to know how long scrolling takes (and avoid it).
997 This must be redone whenever the frame height changes.
999 We keep the ID costs in a precomputed array based on the position
1000 at which the I or D is performed. Also, there are two kinds of ID
1001 costs: the "once-only" and the "repeated". This is to handle both
1002 those terminals that are able to insert N lines at a time (once-
1003 only) and those that must repeatedly insert one line.
1005 The cost to insert N lines at line L is
1006 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
1007 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
1009 ILov represents the basic insert line overhead. ILpf is the padding
1010 required to allow the terminal time to move a line: insertion at line
1011 L changes (frame_height + 1 - L) lines.
1013 The first bracketed expression above is the overhead; the second is
1014 the multiply factor. Both are dependent only on the position at
1015 which the insert is performed. We store the overhead in
1016 FRAME_INSERT_COST (frame) and the multiply factor in
1017 FRAME_INSERTN_COST (frame). Note however that any insertion
1018 must include at least one multiply factor. Rather than compute this
1019 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1020 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
1021 This is reasonable because of the particular algorithm used in calcM.
1023 Deletion is essentially the same as insertion.
1026 void
1027 do_line_insertion_deletion_costs (frame,
1028 ins_line_string, multi_ins_string,
1029 del_line_string, multi_del_string,
1030 setup_string, cleanup_string, coefficient)
1031 FRAME_PTR frame;
1032 char *ins_line_string, *multi_ins_string;
1033 char *del_line_string, *multi_del_string;
1034 char *setup_string, *cleanup_string;
1035 int coefficient;
1037 if (FRAME_INSERT_COST (frame) != 0)
1039 FRAME_INSERT_COST (frame) =
1040 (int *) xrealloc (FRAME_INSERT_COST (frame),
1041 FRAME_HEIGHT (frame) * sizeof (int));
1042 FRAME_DELETEN_COST (frame) =
1043 (int *) xrealloc (FRAME_DELETEN_COST (frame),
1044 FRAME_HEIGHT (frame) * sizeof (int));
1045 FRAME_INSERTN_COST (frame) =
1046 (int *) xrealloc (FRAME_INSERTN_COST (frame),
1047 FRAME_HEIGHT (frame) * sizeof (int));
1048 FRAME_DELETE_COST (frame) =
1049 (int *) xrealloc (FRAME_DELETE_COST (frame),
1050 FRAME_HEIGHT (frame) * sizeof (int));
1052 else
1054 FRAME_INSERT_COST (frame) =
1055 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1056 FRAME_DELETEN_COST (frame) =
1057 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1058 FRAME_INSERTN_COST (frame) =
1059 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1060 FRAME_DELETE_COST (frame) =
1061 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1064 ins_del_costs (frame,
1065 ins_line_string, multi_ins_string,
1066 setup_string, cleanup_string,
1067 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
1068 coefficient);
1069 ins_del_costs (frame,
1070 del_line_string, multi_del_string,
1071 setup_string, cleanup_string,
1072 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
1073 coefficient);