]> code.delx.au - gnu-emacs/blob - src/scroll.c
Merged in changes from CVS trunk. Plus added lisp/term tweaks.
[gnu-emacs] / src / scroll.c
1 /* Calculate what line insertion or deletion to do, and do it,
2 Copyright (C) 1985, 1986, 1990, 1993, 1994, 2002, 2003, 2004,
3 2005 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA. */
21
22
23 #include <config.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "lisp.h"
27 #include "termchar.h"
28 #include "dispextern.h"
29 #include "keyboard.h"
30 #include "frame.h"
31 #include "window.h"
32 #include "termhooks.h"
33
34 /* All costs measured in characters.
35 So no cost can exceed the area of a frame, measured in characters.
36 Let's hope this is never more than 1000000 characters. */
37
38 #define INFINITY 1000000
39
40 struct matrix_elt
41 {
42 /* Cost of outputting through this line
43 if no insert/delete is done just above it. */
44 int writecost;
45 /* Cost of outputting through this line
46 if an insert is done just above it. */
47 int insertcost;
48 /* Cost of outputting through this line
49 if a delete is done just above it. */
50 int deletecost;
51 /* Number of inserts so far in this run of inserts,
52 for the cost in insertcost. */
53 unsigned char insertcount;
54 /* Number of deletes so far in this run of deletes,
55 for the cost in deletecost. */
56 unsigned char deletecount;
57 /* Number of writes so far since the last insert
58 or delete for the cost in writecost. */
59 unsigned char writecount;
60 };
61
62 static void do_direct_scrolling P_ ((struct frame *,
63 struct glyph_matrix *,
64 struct matrix_elt *,
65 int, int));
66 static void do_scrolling P_ ((struct frame *,
67 struct glyph_matrix *,
68 struct matrix_elt *,
69 int, int));
70
71 \f
72 /* Determine, in matrix[i,j], the cost of updating the first j old
73 lines into the first i new lines using the general scrolling method.
74 This involves using insert or delete somewhere if i != j.
75 For each matrix elements, three kinds of costs are recorded:
76 the smallest cost that ends with an insert, the smallest
77 cost that ends with a delete, and the smallest cost that
78 ends with neither one. These are kept separate because
79 on some terminals the cost of doing an insert varies
80 depending on whether one was just done, etc. */
81
82 /* draw_cost[VPOS] is the cost of outputting new line at VPOS.
83 old_hash[VPOS] is the hash code of the old line at VPOS.
84 new_hash[VPOS] is the hash code of the new line at VPOS.
85 Note that these are not true frame vpos's, but relative
86 to the place at which the first mismatch between old and
87 new contents appears. */
88
89 static void
90 calculate_scrolling (frame, matrix, window_size, lines_below,
91 draw_cost, old_hash, new_hash,
92 free_at_end)
93 FRAME_PTR frame;
94 /* matrix is of size window_size + 1 on each side. */
95 struct matrix_elt *matrix;
96 int window_size, lines_below;
97 int *draw_cost;
98 int *old_hash;
99 int *new_hash;
100 int free_at_end;
101 {
102 register int i, j;
103 int frame_lines = FRAME_LINES (frame);
104 register struct matrix_elt *p, *p1;
105 register int cost, cost1;
106
107 int lines_moved = window_size
108 + (FRAME_SCROLL_REGION_OK (frame) ? 0 : lines_below);
109 /* first_insert_cost[I] is the cost of doing the first insert-line
110 at the i'th line of the lines we are considering,
111 where I is origin 1 (as it is below). */
112 int *first_insert_cost
113 = &FRAME_INSERT_COST (frame)[frame_lines - 1 - lines_moved];
114 int *first_delete_cost
115 = &FRAME_DELETE_COST (frame)[frame_lines - 1 - lines_moved];
116 int *next_insert_cost
117 = &FRAME_INSERTN_COST (frame)[frame_lines - 1 - lines_moved];
118 int *next_delete_cost
119 = &FRAME_DELETEN_COST (frame)[frame_lines - 1 - lines_moved];
120
121 /* Discourage long scrolls on fast lines.
122 Don't scroll nearly a full frame height unless it saves
123 at least 1/4 second. */
124 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
125
126 if (baud_rate <= 0)
127 extra_cost = 1;
128
129 /* initialize the top left corner of the matrix */
130 matrix->writecost = 0;
131 matrix->insertcost = INFINITY;
132 matrix->deletecost = INFINITY;
133 matrix->insertcount = 0;
134 matrix->deletecount = 0;
135
136 /* initialize the left edge of the matrix */
137 cost = first_insert_cost[1] - next_insert_cost[1];
138 for (i = 1; i <= window_size; i++)
139 {
140 p = matrix + i * (window_size + 1);
141 cost += draw_cost[i] + next_insert_cost[i] + extra_cost;
142 p->insertcost = cost;
143 p->writecost = INFINITY;
144 p->deletecost = INFINITY;
145 p->insertcount = i;
146 p->deletecount = 0;
147 }
148
149 /* initialize the top edge of the matrix */
150 cost = first_delete_cost[1] - next_delete_cost[1];
151 for (j = 1; j <= window_size; j++)
152 {
153 cost += next_delete_cost[j];
154 matrix[j].deletecost = cost;
155 matrix[j].writecost = INFINITY;
156 matrix[j].insertcost = INFINITY;
157 matrix[j].deletecount = j;
158 matrix[j].insertcount = 0;
159 }
160
161 /* `i' represents the vpos among new frame contents.
162 `j' represents the vpos among the old frame contents. */
163 p = matrix + window_size + 2; /* matrix [1, 1] */
164 for (i = 1; i <= window_size; i++, p++)
165 for (j = 1; j <= window_size; j++, p++)
166 {
167 /* p contains the address of matrix [i, j] */
168
169 /* First calculate the cost assuming we do
170 not insert or delete above this line.
171 That is, if we update through line i-1
172 based on old lines through j-1,
173 and then just change old line j to new line i. */
174 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
175 cost = p1->writecost;
176 if (cost > p1->insertcost)
177 cost = p1->insertcost;
178 if (cost > p1->deletecost)
179 cost = p1->deletecost;
180 if (old_hash[j] != new_hash[i])
181 cost += draw_cost[i];
182 p->writecost = cost;
183
184 /* Calculate the cost if we do an insert-line
185 before outputting this line.
186 That is, we update through line i-1
187 based on old lines through j,
188 do an insert-line on line i,
189 and then output line i from scratch,
190 leaving old lines starting from j for reuse below. */
191 p1 = p - window_size - 1; /* matrix [i-1, j] */
192 /* No need to think about doing a delete followed
193 immediately by an insert. It cannot be as good
194 as not doing either of them. */
195 if (free_at_end == i)
196 {
197 cost = p1->writecost;
198 cost1 = p1->insertcost;
199 }
200 else
201 {
202 cost = p1->writecost + first_insert_cost[i];
203 if ((int) p1->insertcount > i)
204 abort ();
205 cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
206 }
207 p->insertcost = min (cost, cost1) + draw_cost[i] + extra_cost;
208 p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
209 if ((int) p->insertcount > i)
210 abort ();
211
212 /* Calculate the cost if we do a delete line after
213 outputting this line.
214 That is, we update through line i
215 based on old lines through j-1,
216 and throw away old line j. */
217 p1 = p - 1; /* matrix [i, j-1] */
218 /* No need to think about doing an insert followed
219 immediately by a delete. */
220 if (free_at_end == i)
221 {
222 cost = p1->writecost;
223 cost1 = p1->deletecost;
224 }
225 else
226 {
227 cost = p1->writecost + first_delete_cost[i];
228 cost1 = p1->deletecost + next_delete_cost[i];
229 }
230 p->deletecost = min (cost, cost1);
231 p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
232 }
233 }
234
235
236 \f
237 /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
238 according to the costs in MATRIX, using the general scrolling
239 method that is used if the terminal does not support the setting of
240 scroll windows (scroll_region_ok == 0).
241
242 WINDOW_SIZE is the number of lines being considered for scrolling
243 and UNCHANGED_AT_TOP is the vpos of the first line being
244 considered. These two arguments can specify any contiguous range
245 of lines. */
246
247 static void
248 do_scrolling (frame, current_matrix, matrix, window_size, unchanged_at_top)
249 struct frame *frame;
250 struct glyph_matrix *current_matrix;
251 struct matrix_elt *matrix;
252 int window_size;
253 int unchanged_at_top;
254 {
255 struct matrix_elt *p;
256 int i, j, k;
257
258 /* Set to 1 if we have set a terminal window with
259 set_terminal_window. */
260 int terminal_window_p = 0;
261
262 /* A queue for line insertions to be done. */
263 struct queue { int count, pos; };
264 struct queue *queue_start
265 = (struct queue *) alloca (current_matrix->nrows * sizeof (struct queue));
266 struct queue *queue = queue_start;
267
268 char *retained_p = (char *) alloca (window_size * sizeof (char));
269 int *copy_from = (int *) alloca (window_size * sizeof (int));
270
271 /* Zero means line is empty. */
272 bzero (retained_p, window_size * sizeof (char));
273 for (k = 0; k < window_size; ++k)
274 copy_from[k] = -1;
275
276 #define CHECK_BOUNDS \
277 do \
278 { \
279 int k; \
280 for (k = 0; k < window_size; ++k) \
281 xassert (copy_from[k] == -1 \
282 || (copy_from[k] >= 0 && copy_from[k] < window_size)); \
283 } \
284 while (0);
285
286 /* When j is advanced, this corresponds to deleted lines.
287 When i is advanced, this corresponds to inserted lines. */
288 i = j = window_size;
289 while (i > 0 || j > 0)
290 {
291 p = matrix + i * (window_size + 1) + j;
292
293 if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
294 {
295 /* Insert should be done at vpos i-1, plus maybe some before.
296 Queue the screen operation to be performed. */
297 queue->count = p->insertcount;
298 queue->pos = i + unchanged_at_top - p->insertcount;
299 ++queue;
300
301 /* By incrementing I, we leave room in the result rows
302 for the empty rows opened up. */
303 i -= p->insertcount;
304 }
305 else if (p->deletecost < p->writecost)
306 {
307 /* Old line at vpos j-1, and maybe some before it, should be
308 deleted. By decrementing J, we skip some lines in the
309 temp_rows which is equivalent to omitting these lines in
310 the result rows, thus deleting them. */
311 j -= p->deletecount;
312
313 /* Set the terminal window, if not done already. */
314 if (! terminal_window_p)
315 {
316 set_terminal_window (frame, window_size + unchanged_at_top);
317 terminal_window_p = 1;
318 }
319
320 /* Delete lines on the terminal. */
321 ins_del_lines (frame, j + unchanged_at_top, - p->deletecount);
322 }
323 else
324 {
325 /* Best thing done here is no insert or delete, i.e. a write. */
326 --i, --j;
327 xassert (i >= 0 && i < window_size);
328 xassert (j >= 0 && j < window_size);
329 copy_from[i] = j;
330 retained_p[j] = 1;
331
332 #if GLYPH_DEBUG
333 CHECK_BOUNDS;
334 #endif
335 }
336 }
337
338 /* Now do all insertions queued above. */
339 if (queue > queue_start)
340 {
341 int next = -1;
342
343 /* Set the terminal window if not yet done. */
344 if (!terminal_window_p)
345 {
346 set_terminal_window (frame, window_size + unchanged_at_top);
347 terminal_window_p = 1;
348 }
349
350 do
351 {
352 --queue;
353
354 /* Do the deletion on the terminal. */
355 ins_del_lines (frame, queue->pos, queue->count);
356
357 /* All lines in the range deleted become empty in the glyph
358 matrix. Assign to them glyph rows that are not retained.
359 K is the starting position of the deleted range relative
360 to the window we are working in. */
361 k = queue->pos - unchanged_at_top;
362 for (j = 0; j < queue->count; ++j)
363 {
364 /* Find the next row not retained. */
365 while (retained_p[++next])
366 ;
367
368 /* Record that this row is to be used for the empty
369 glyph row j. */
370 copy_from[k + j] = next;
371 }
372 }
373 while (queue > queue_start);
374
375 }
376
377 for (k = 0; k < window_size; ++k)
378 xassert (copy_from[k] >= 0 && copy_from[k] < window_size);
379
380 /* Perform the row swizzling. */
381 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
382 copy_from, retained_p);
383
384 /* Some sanity checks if GLYPH_DEBUG != 0. */
385 CHECK_MATRIX (current_matrix);
386
387 if (terminal_window_p)
388 set_terminal_window (frame, 0);
389 }
390
391 \f
392 /* Determine, in matrix[i,j], the cost of updating the first j
393 old lines into the first i new lines using the direct
394 scrolling method. When the old line and the new line have
395 different hash codes, the calculated cost of updating old
396 line j into new line i includes the cost of outputting new
397 line i, and if i != j, the cost of outputting the old line j
398 is also included, as a penalty for moving the line and then
399 erasing it. In addition, the cost of updating a sequence of
400 lines with constant i - j includes the cost of scrolling the
401 old lines into their new positions, unless i == j. Scrolling
402 is achieved by setting the screen window to avoid affecting
403 other lines below, and inserting or deleting lines at the top
404 of the scrolled region. The cost of scrolling a sequence of
405 lines includes the fixed cost of specifying a scroll region,
406 plus a variable cost which can depend upon the number of lines
407 involved and the distance by which they are scrolled, and an
408 extra cost to discourage long scrolls.
409
410 As reflected in the matrix, an insert or delete does not
411 correspond directly to the insertion or deletion which is
412 used in scrolling lines. An insert means that the value of i
413 has increased without a corresponding increase in the value
414 of j. A delete means that the value of j has increased
415 without a corresponding increase in the value of i. A write
416 means that i and j are both increased by the same amount, and
417 that the old lines will be moved to their new positions.
418
419 An insert following a delete is allowed only if i > j.
420 A delete following an insert is allowed only if i < j.
421 These restrictions ensure that the new lines in an insert
422 will always be blank as an effect of the neighboring writes.
423 Thus the calculated cost of an insert is simply the cost of
424 outputting the new line contents. The direct cost of a
425 delete is zero. Inserts and deletes indirectly affect the
426 total cost through their influence on subsequent writes. */
427
428 /* The vectors draw_cost, old_hash, and new_hash have the same
429 meanings here as in calculate_scrolling, and old_draw_cost
430 is the equivalent of draw_cost for the old line contents */
431
432 static void
433 calculate_direct_scrolling (frame, matrix, window_size, lines_below,
434 draw_cost, old_draw_cost, old_hash, new_hash,
435 free_at_end)
436 FRAME_PTR frame;
437 /* matrix is of size window_size + 1 on each side. */
438 struct matrix_elt *matrix;
439 int window_size, lines_below;
440 int *draw_cost;
441 int *old_draw_cost;
442 int *old_hash;
443 int *new_hash;
444 int free_at_end;
445 {
446 register int i, j;
447 int frame_lines = FRAME_LINES (frame);
448 register struct matrix_elt *p, *p1;
449 register int cost, cost1, delta;
450
451 /* first_insert_cost[-I] is the cost of doing the first insert-line
452 at a position I lines above the bottom line in the scroll window. */
453 int *first_insert_cost
454 = &FRAME_INSERT_COST (frame)[frame_lines - 1];
455 int *first_delete_cost
456 = &FRAME_DELETE_COST (frame)[frame_lines - 1];
457 int *next_insert_cost
458 = &FRAME_INSERTN_COST (frame)[frame_lines - 1];
459 int *next_delete_cost
460 = &FRAME_DELETEN_COST (frame)[frame_lines - 1];
461
462 int scroll_overhead;
463
464 /* Discourage long scrolls on fast lines.
465 Don't scroll nearly a full frame height unless it saves
466 at least 1/4 second. */
467 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
468
469 if (baud_rate <= 0)
470 extra_cost = 1;
471
472 /* Overhead of setting the scroll window, plus the extra cost
473 cost of scrolling by a distance of one. The extra cost is
474 added once for consistency with the cost vectors */
475 scroll_overhead
476 = FRAME_SCROLL_REGION_COST (frame) + extra_cost;
477
478 /* initialize the top left corner of the matrix */
479 matrix->writecost = 0;
480 matrix->insertcost = INFINITY;
481 matrix->deletecost = INFINITY;
482 matrix->writecount = 0;
483 matrix->insertcount = 0;
484 matrix->deletecount = 0;
485
486 /* initialize the left edge of the matrix */
487 cost = 0;
488 for (i = 1; i <= window_size; i++)
489 {
490 p = matrix + i * (window_size + 1);
491 cost += draw_cost[i];
492 p->insertcost = cost;
493 p->writecost = INFINITY;
494 p->deletecost = INFINITY;
495 p->insertcount = i;
496 p->writecount = 0;
497 p->deletecount = 0;
498 }
499
500 /* initialize the top edge of the matrix */
501 for (j = 1; j <= window_size; j++)
502 {
503 matrix[j].deletecost = 0;
504 matrix[j].writecost = INFINITY;
505 matrix[j].insertcost = INFINITY;
506 matrix[j].deletecount = j;
507 matrix[j].writecount = 0;
508 matrix[j].insertcount = 0;
509 }
510
511 /* `i' represents the vpos among new frame contents.
512 `j' represents the vpos among the old frame contents. */
513 p = matrix + window_size + 2; /* matrix [1, 1] */
514
515 for (i = 1; i <= window_size; i++, p++)
516 for (j = 1; j <= window_size; j++, p++)
517 {
518 /* p contains the address of matrix [i, j] */
519
520 /* First calculate the cost assuming we do
521 not insert or delete above this line.
522 That is, if we update through line i-1
523 based on old lines through j-1,
524 and then just change old line j to new line i.
525
526 Depending on which choice gives the lower cost,
527 this usually involves either scrolling a single line
528 or extending a sequence of scrolled lines, but
529 when i == j, no scrolling is required. */
530 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
531 cost = p1->insertcost;
532 if (cost > p1->deletecost)
533 cost = p1->deletecost;
534 cost1 = p1->writecost;
535 if (i == j)
536 {
537 if (cost > cost1)
538 {
539 cost = cost1;
540 p->writecount = p1->writecount + 1;
541 }
542 else
543 p->writecount = 1;
544 if (old_hash[j] != new_hash[i])
545 {
546 cost += draw_cost[i];
547 }
548 }
549 else
550 {
551 if (i > j)
552 {
553 delta = i - j;
554
555 /* The cost added here for scrolling the first line by
556 a distance N includes the overhead of setting the
557 scroll window, the cost of inserting N lines at a
558 position N lines above the bottom line of the window,
559 and an extra cost which is proportional to N. */
560 cost += scroll_overhead + first_insert_cost[-delta] +
561 (delta-1) * (next_insert_cost[-delta] + extra_cost);
562
563 /* In the most general case, the insertion overhead and
564 the multiply factor can grow linearly as the distance
565 from the bottom of the window increases. The incremental
566 cost of scrolling an additional line depends upon the
567 rate of change of these two parameters. Each of these
568 growth rates can be determined by a simple difference.
569 To reduce the cumulative effects of rounding error, we
570 vary the position at which the difference is computed. */
571 cost1 += first_insert_cost[-j] - first_insert_cost[1-j] +
572 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
573 }
574 else
575 {
576 delta = j - i;
577 cost += scroll_overhead + first_delete_cost[-delta] +
578 (delta-1) * (next_delete_cost[-delta] + extra_cost);
579 cost1 += first_delete_cost[-i] - first_delete_cost[1-i] +
580 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
581 }
582 if (cost1 < cost)
583 {
584 cost = cost1;
585 p->writecount = p1->writecount + 1;
586 }
587 else
588 p->writecount = 1;
589 if (old_hash[j] != new_hash[i])
590 {
591 cost += draw_cost[i] + old_draw_cost[j];
592 }
593 }
594 p->writecost = cost;
595
596 /* Calculate the cost if we do an insert-line
597 before outputting this line.
598 That is, we update through line i-1
599 based on old lines through j,
600 do an insert-line on line i,
601 and then output line i from scratch,
602 leaving old lines starting from j for reuse below. */
603 p1 = p - window_size - 1; /* matrix [i-1, j] */
604 cost = p1->writecost;
605 /* If i > j, an insert is allowed after a delete. */
606 if (i > j && p1->deletecost < cost)
607 cost = p1->deletecost;
608 if (p1->insertcost <= cost)
609 {
610 cost = p1->insertcost;
611 p->insertcount = p1->insertcount + 1;
612 }
613 else
614 p->insertcount = 1;
615 cost += draw_cost[i];
616 p->insertcost = cost;
617
618 /* Calculate the cost if we do a delete line after
619 outputting this line.
620 That is, we update through line i
621 based on old lines through j-1,
622 and throw away old line j. */
623 p1 = p - 1; /* matrix [i, j-1] */
624 cost = p1->writecost;
625 /* If i < j, a delete is allowed after an insert. */
626 if (i < j && p1->insertcost < cost)
627 cost = p1->insertcost;
628 cost1 = p1->deletecost;
629 if (p1->deletecost <= cost)
630 {
631 cost = p1->deletecost;
632 p->deletecount = p1->deletecount + 1;
633 }
634 else
635 p->deletecount = 1;
636 p->deletecost = cost;
637 }
638 }
639
640
641 \f
642 /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
643 according to the costs in MATRIX, using the direct scrolling method
644 which is used when the terminal supports setting a scroll window
645 (scroll_region_ok).
646
647 WINDOW_SIZE is the number of lines being considered for scrolling
648 and UNCHANGED_AT_TOP is the vpos of the first line being
649 considered. These two arguments can specify any contiguous range
650 of lines.
651
652 In the direct scrolling method, a new scroll window is selected
653 before each insertion or deletion, so that groups of lines can be
654 scrolled directly to their final vertical positions. This method
655 is described in more detail in calculate_direct_scrolling, where
656 the cost matrix for this approach is constructed. */
657
658 static void
659 do_direct_scrolling (frame, current_matrix, cost_matrix,
660 window_size, unchanged_at_top)
661 struct frame *frame;
662 struct glyph_matrix *current_matrix;
663 struct matrix_elt *cost_matrix;
664 int window_size;
665 int unchanged_at_top;
666 {
667 struct matrix_elt *p;
668 int i, j;
669
670 /* A queue of deletions and insertions to be performed. */
671 struct alt_queue { int count, pos, window; };
672 struct alt_queue *queue_start = (struct alt_queue *)
673 alloca (window_size * sizeof *queue_start);
674 struct alt_queue *queue = queue_start;
675
676 /* Set to 1 if a terminal window has been set with
677 set_terminal_window: */
678 int terminal_window_p = 0;
679
680 /* A nonzero value of write_follows indicates that a write has been
681 selected, allowing either an insert or a delete to be selected
682 next. When write_follows is zero, a delete cannot be selected
683 unless j < i, and an insert cannot be selected unless i < j.
684 This corresponds to a similar restriction (with the ordering
685 reversed) in calculate_direct_scrolling, which is intended to
686 ensure that lines marked as inserted will be blank. */
687 int write_follows_p = 1;
688
689 /* For each row in the new matrix what row of the old matrix it is. */
690 int *copy_from = (int *) alloca (window_size * sizeof (int));
691
692 /* Non-zero for each row in the new matrix that is retained from the
693 old matrix. Lines not retained are empty. */
694 char *retained_p = (char *) alloca (window_size * sizeof (char));
695
696 bzero (retained_p, window_size * sizeof (char));
697
698 /* Perform some sanity checks when GLYPH_DEBUG is on. */
699 CHECK_MATRIX (current_matrix);
700
701 /* We are working on the line range UNCHANGED_AT_TOP ...
702 UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX.
703 We step through lines in this range from the end to the start. I
704 is an index into new lines, j an index into old lines. The cost
705 matrix determines what to do for ranges of indices.
706
707 If i is decremented without also decrementing j, this corresponds
708 to inserting empty lines in the result. If j is decremented
709 without also decrementing i, this corresponds to omitting these
710 lines in the new rows, i.e. rows are deleted. */
711 i = j = window_size;
712
713 while (i > 0 || j > 0)
714 {
715 p = cost_matrix + i * (window_size + 1) + j;
716
717 if (p->insertcost < p->writecost
718 && p->insertcost < p->deletecost
719 && (write_follows_p || i < j))
720 {
721 /* Insert is cheaper than deleting or writing lines. Leave
722 a hole in the result display that will be filled with
723 empty lines when the queue is emptied. */
724 queue->count = 0;
725 queue->window = i;
726 queue->pos = i - p->insertcount;
727 ++queue;
728
729 i -= p->insertcount;
730 write_follows_p = 0;
731 }
732 else if (p->deletecost < p->writecost
733 && (write_follows_p || i > j))
734 {
735 /* Deleting lines is cheaper. By decrementing J, omit
736 deletecount lines from the original. */
737 write_follows_p = 0;
738 j -= p->deletecount;
739 }
740 else
741 {
742 /* One or more lines should be written. In the direct
743 scrolling method we do this by scrolling the lines to the
744 place they belong. */
745 int n_to_write = p->writecount;
746 write_follows_p = 1;
747 xassert (n_to_write > 0);
748
749 if (i > j)
750 {
751 /* Immediately insert lines */
752 set_terminal_window (frame, i + unchanged_at_top);
753 terminal_window_p = 1;
754 ins_del_lines (frame, j - n_to_write + unchanged_at_top, i - j);
755 }
756 else if (i < j)
757 {
758 /* Queue the deletion of a group of lines */
759 queue->pos = i - n_to_write + unchanged_at_top;
760 queue->window = j + unchanged_at_top;
761 queue->count = i - j;
762 ++queue;
763 }
764
765 while (n_to_write > 0)
766 {
767 --i, --j, --n_to_write;
768 copy_from[i] = j;
769 retained_p[j] = 1;
770 }
771 }
772 }
773
774 /* Do queued operations. */
775 if (queue > queue_start)
776 {
777 int next = -1;
778
779 do
780 {
781 --queue;
782 if (queue->count)
783 {
784 set_terminal_window (frame, queue->window);
785 terminal_window_p = 1;
786 ins_del_lines (frame, queue->pos, queue->count);
787 }
788 else
789 {
790 for (j = queue->window - 1; j >= queue->pos; --j)
791 {
792 while (retained_p[++next])
793 ;
794 copy_from[j] = next;
795 }
796 }
797 }
798 while (queue > queue_start);
799 }
800
801 /* Now, for each row I in the range of rows we are working on,
802 copy_from[i] gives the original line to copy to I, and
803 retained_p[copy_from[i]] is zero if line I in the new display is
804 empty. */
805 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
806 copy_from, retained_p);
807
808 if (terminal_window_p)
809 set_terminal_window (frame, 0);
810 }
811
812
813 \f
814 void
815 scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
816 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
817 FRAME_PTR frame;
818 int window_size, unchanged_at_top, unchanged_at_bottom;
819 int *draw_cost;
820 int *old_draw_cost;
821 int *old_hash;
822 int *new_hash;
823 int free_at_end;
824 {
825 struct matrix_elt *matrix;
826 matrix = ((struct matrix_elt *)
827 alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
828
829 if (FRAME_SCROLL_REGION_OK (frame))
830 {
831 calculate_direct_scrolling (frame, matrix, window_size,
832 unchanged_at_bottom,
833 draw_cost, old_draw_cost,
834 old_hash, new_hash, free_at_end);
835 do_direct_scrolling (frame, frame->current_matrix,
836 matrix, window_size, unchanged_at_top);
837 }
838 else
839 {
840 calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
841 draw_cost, old_hash, new_hash,
842 free_at_end);
843 do_scrolling (frame,
844 frame->current_matrix, matrix, window_size,
845 unchanged_at_top);
846 }
847 }
848
849
850 \f
851 /* Return number of lines in common between current and desired frame
852 contents described to us only as vectors of hash codes OLDHASH and
853 NEWHASH. Consider only vpos range START to END (not including
854 END). Ignore short lines on the assumption that avoiding redrawing
855 such a line will have little weight. */
856
857 int
858 scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
859 int start, end;
860 int *oldhash, *newhash, *cost;
861 {
862 struct { int hash; int count; } lines[01000];
863 register int i, h;
864 register int matchcount = 0;
865 int avg_length = 0;
866 int threshold;
867
868 /* Compute a threshold which is 1/4 of average length of these lines. */
869
870 for (i = start; i < end; i++)
871 avg_length += cost[i];
872
873 avg_length /= end - start;
874 threshold = avg_length / 4;
875
876 bzero (lines, sizeof lines);
877
878 /* Put new lines' hash codes in hash table. Ignore lines shorter
879 than the threshold. Thus, if the lines that are in common are
880 mainly the ones that are short, they won't count. */
881 for (i = start; i < end; i++)
882 {
883 if (cost[i] > threshold)
884 {
885 h = newhash[i] & 0777;
886 lines[h].hash = newhash[i];
887 lines[h].count++;
888 }
889 }
890
891 /* Look up old line hash codes in the hash table. Count number of
892 matches between old lines and new. */
893 for (i = start; i < end; i++)
894 {
895 h = oldhash[i] & 0777;
896 if (oldhash[i] == lines[h].hash)
897 {
898 matchcount++;
899 if (--lines[h].count == 0)
900 lines[h].hash = 0;
901 }
902 }
903
904 return matchcount;
905 }
906 \f
907 /* Return a measure of the cost of moving the lines starting with vpos
908 FROM, up to but not including vpos TO, down by AMOUNT lines (AMOUNT
909 may be negative). These are the same arguments that might be given
910 to scroll_frame_lines to perform this scrolling. */
911
912 int
913 scroll_cost (frame, from, to, amount)
914 FRAME_PTR frame;
915 int from, to, amount;
916 {
917 /* Compute how many lines, at bottom of frame,
918 will not be involved in actual motion. */
919 int limit = to;
920 int offset;
921 int height = FRAME_LINES (frame);
922
923 if (amount == 0)
924 return 0;
925
926 if (! FRAME_SCROLL_REGION_OK (frame))
927 limit = height;
928 else if (amount > 0)
929 limit += amount;
930
931 if (amount < 0)
932 {
933 int temp = to;
934 to = from + amount;
935 from = temp + amount;
936 amount = - amount;
937 }
938
939 offset = height - limit;
940
941 return
942 (FRAME_INSERT_COST (frame)[offset + from]
943 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
944 + FRAME_DELETE_COST (frame)[offset + to]
945 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
946 }
947 \f
948 /* Calculate the line insertion/deletion
949 overhead and multiply factor values */
950
951 static void
952 line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
953 FRAME_PTR frame;
954 int ov1, ovn;
955 int pf1, pfn;
956 register int *ov, *mf;
957 {
958 register int i;
959 register int frame_lines = FRAME_LINES (frame);
960 register int insert_overhead = ov1 * 10;
961 register int next_insert_cost = ovn * 10;
962
963 for (i = frame_lines-1; i >= 0; i--)
964 {
965 mf[i] = next_insert_cost / 10;
966 next_insert_cost += pfn;
967 ov[i] = (insert_overhead + next_insert_cost) / 10;
968 insert_overhead += pf1;
969 }
970 }
971
972 static void
973 ins_del_costs (frame,
974 one_line_string, multi_string,
975 setup_string, cleanup_string,
976 costvec, ncostvec, coefficient)
977 FRAME_PTR frame;
978 char *one_line_string, *multi_string;
979 char *setup_string, *cleanup_string;
980 int *costvec, *ncostvec;
981 int coefficient;
982 {
983 if (multi_string)
984 line_ins_del (frame,
985 string_cost (multi_string) * coefficient,
986 per_line_cost (multi_string) * coefficient,
987 0, 0, costvec, ncostvec);
988 else if (one_line_string)
989 line_ins_del (frame,
990 string_cost (setup_string) + string_cost (cleanup_string), 0,
991 string_cost (one_line_string),
992 per_line_cost (one_line_string),
993 costvec, ncostvec);
994 else
995 line_ins_del (frame,
996 9999, 0, 9999, 0,
997 costvec, ncostvec);
998 }
999
1000 /* Calculate the insert and delete line costs.
1001 Note that this is done even when running with a window system
1002 because we want to know how long scrolling takes (and avoid it).
1003 This must be redone whenever the frame height changes.
1004
1005 We keep the ID costs in a precomputed array based on the position
1006 at which the I or D is performed. Also, there are two kinds of ID
1007 costs: the "once-only" and the "repeated". This is to handle both
1008 those terminals that are able to insert N lines at a time (once-
1009 only) and those that must repeatedly insert one line.
1010
1011 The cost to insert N lines at line L is
1012 [tt.t_ILov + (frame_lines + 1 - L) * tt.t_ILpf] +
1013 N * [tt.t_ILnov + (frame_lines + 1 - L) * tt.t_ILnpf]
1014
1015 ILov represents the basic insert line overhead. ILpf is the padding
1016 required to allow the terminal time to move a line: insertion at line
1017 L changes (frame_lines + 1 - L) lines.
1018
1019 The first bracketed expression above is the overhead; the second is
1020 the multiply factor. Both are dependent only on the position at
1021 which the insert is performed. We store the overhead in
1022 FRAME_INSERT_COST (frame) and the multiply factor in
1023 FRAME_INSERTN_COST (frame). Note however that any insertion
1024 must include at least one multiply factor. Rather than compute this
1025 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1026 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
1027 This is reasonable because of the particular algorithm used in calcM.
1028
1029 Deletion is essentially the same as insertion.
1030 */
1031
1032 void
1033 do_line_insertion_deletion_costs (frame,
1034 ins_line_string, multi_ins_string,
1035 del_line_string, multi_del_string,
1036 setup_string, cleanup_string, coefficient)
1037 FRAME_PTR frame;
1038 char *ins_line_string, *multi_ins_string;
1039 char *del_line_string, *multi_del_string;
1040 char *setup_string, *cleanup_string;
1041 int coefficient;
1042 {
1043 if (FRAME_INSERT_COST (frame) != 0)
1044 {
1045 FRAME_INSERT_COST (frame) =
1046 (int *) xrealloc (FRAME_INSERT_COST (frame),
1047 FRAME_LINES (frame) * sizeof (int));
1048 FRAME_DELETEN_COST (frame) =
1049 (int *) xrealloc (FRAME_DELETEN_COST (frame),
1050 FRAME_LINES (frame) * sizeof (int));
1051 FRAME_INSERTN_COST (frame) =
1052 (int *) xrealloc (FRAME_INSERTN_COST (frame),
1053 FRAME_LINES (frame) * sizeof (int));
1054 FRAME_DELETE_COST (frame) =
1055 (int *) xrealloc (FRAME_DELETE_COST (frame),
1056 FRAME_LINES (frame) * sizeof (int));
1057 }
1058 else
1059 {
1060 FRAME_INSERT_COST (frame) =
1061 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
1062 FRAME_DELETEN_COST (frame) =
1063 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
1064 FRAME_INSERTN_COST (frame) =
1065 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
1066 FRAME_DELETE_COST (frame) =
1067 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
1068 }
1069
1070 ins_del_costs (frame,
1071 ins_line_string, multi_ins_string,
1072 setup_string, cleanup_string,
1073 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
1074 coefficient);
1075 ins_del_costs (frame,
1076 del_line_string, multi_del_string,
1077 setup_string, cleanup_string,
1078 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
1079 coefficient);
1080 }
1081
1082 /* arch-tag: cdb7149c-48e7-4793-a948-2786c8e45485
1083 (do not change this comment) */