Merged in changes from CVS trunk.
[bpt/emacs.git] / src / scroll.c
CommitLineData
632a8dd6 1/* Calculate what line insertion or deletion to do, and do it,
3a22ee35 2 Copyright (C) 1985, 1986, 1990, 1993, 1994 Free Software Foundation, Inc.
632a8dd6
JB
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
e5d77022 8the Free Software Foundation; either version 2, or (at your option)
632a8dd6
JB
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
3b7ad313
EN
18the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
632a8dd6
JB
20
21
18160b98 22#include <config.h>
89700111
GM
23#include <stdio.h>
24#include <string.h>
9628b887 25#include "lisp.h"
632a8dd6 26#include "termchar.h"
632a8dd6 27#include "dispextern.h"
2538fae4 28#include "keyboard.h"
0137dbf7 29#include "frame.h"
89700111 30#include "window.h"
daf01701 31#include "termhooks.h"
632a8dd6 32
632a8dd6 33/* All costs measured in characters.
0137dbf7 34 So no cost can exceed the area of a frame, measured in characters.
61df1a5e 35 Let's hope this is never more than 1000000 characters. */
632a8dd6 36
61df1a5e 37#define INFINITY 1000000
632a8dd6
JB
38
39struct matrix_elt
40 {
41 /* Cost of outputting through this line
42 if no insert/delete is done just above it. */
61df1a5e 43 int writecost;
632a8dd6
JB
44 /* Cost of outputting through this line
45 if an insert is done just above it. */
61df1a5e 46 int insertcost;
632a8dd6
JB
47 /* Cost of outputting through this line
48 if a delete is done just above it. */
61df1a5e 49 int deletecost;
632a8dd6
JB
50 /* Number of inserts so far in this run of inserts,
51 for the cost in insertcost. */
61df1a5e 52 unsigned char insertcount;
632a8dd6
JB
53 /* Number of deletes so far in this run of deletes,
54 for the cost in deletecost. */
61df1a5e 55 unsigned char deletecount;
1af1b1e8
RS
56 /* Number of writes so far since the last insert
57 or delete for the cost in writecost. */
58 unsigned char writecount;
632a8dd6
JB
59 };
60
89700111
GM
61static void do_direct_scrolling P_ ((struct glyph_matrix *,
62 struct matrix_elt *,
63 int, int));
64static void do_scrolling P_ ((struct glyph_matrix *,
65 struct matrix_elt *,
66 int, int));
67
632a8dd6 68\f
1af1b1e8
RS
69/* Determine, in matrix[i,j], the cost of updating the first j old
70 lines into the first i new lines using the general scrolling method.
632a8dd6
JB
71 This involves using insert or delete somewhere if i != j.
72 For each matrix elements, three kinds of costs are recorded:
73 the smallest cost that ends with an insert, the smallest
74 cost that ends with a delete, and the smallest cost that
75 ends with neither one. These are kept separate because
76 on some terminals the cost of doing an insert varies
77 depending on whether one was just done, etc. */
78
79/* draw_cost[VPOS] is the cost of outputting new line at VPOS.
80 old_hash[VPOS] is the hash code of the old line at VPOS.
81 new_hash[VPOS] is the hash code of the new line at VPOS.
0137dbf7 82 Note that these are not true frame vpos's, but relative
632a8dd6
JB
83 to the place at which the first mismatch between old and
84 new contents appears. */
85
86static void
0137dbf7 87calculate_scrolling (frame, matrix, window_size, lines_below,
632a8dd6
JB
88 draw_cost, old_hash, new_hash,
89 free_at_end)
0137dbf7 90 FRAME_PTR frame;
632a8dd6
JB
91 /* matrix is of size window_size + 1 on each side. */
92 struct matrix_elt *matrix;
250368af 93 int window_size, lines_below;
632a8dd6
JB
94 int *draw_cost;
95 int *old_hash;
96 int *new_hash;
97 int free_at_end;
98{
99 register int i, j;
9882535b 100 int frame_lines = FRAME_LINES (frame);
632a8dd6
JB
101 register struct matrix_elt *p, *p1;
102 register int cost, cost1;
103
8a56675d 104 int lines_moved = window_size
daf01701 105 + (FRAME_SCROLL_REGION_OK (frame) ? 0 : lines_below);
632a8dd6 106 /* first_insert_cost[I] is the cost of doing the first insert-line
89700111 107 at the i'th line of the lines we are considering,
632a8dd6
JB
108 where I is origin 1 (as it is below). */
109 int *first_insert_cost
9882535b 110 = &FRAME_INSERT_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 111 int *first_delete_cost
9882535b 112 = &FRAME_DELETE_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 113 int *next_insert_cost
9882535b 114 = &FRAME_INSERTN_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 115 int *next_delete_cost
9882535b 116 = &FRAME_DELETEN_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6
JB
117
118 /* Discourage long scrolls on fast lines.
0137dbf7 119 Don't scroll nearly a full frame height unless it saves
632a8dd6 120 at least 1/4 second. */
9882535b 121 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
632a8dd6 122
7fb08f69
RS
123 if (baud_rate <= 0)
124 extra_cost = 1;
125
632a8dd6
JB
126 /* initialize the top left corner of the matrix */
127 matrix->writecost = 0;
128 matrix->insertcost = INFINITY;
129 matrix->deletecost = INFINITY;
130 matrix->insertcount = 0;
131 matrix->deletecount = 0;
132
133 /* initialize the left edge of the matrix */
134 cost = first_insert_cost[1] - next_insert_cost[1];
135 for (i = 1; i <= window_size; i++)
136 {
137 p = matrix + i * (window_size + 1);
138 cost += draw_cost[i] + next_insert_cost[i] + extra_cost;
139 p->insertcost = cost;
140 p->writecost = INFINITY;
141 p->deletecost = INFINITY;
142 p->insertcount = i;
143 p->deletecount = 0;
144 }
145
146 /* initialize the top edge of the matrix */
147 cost = first_delete_cost[1] - next_delete_cost[1];
148 for (j = 1; j <= window_size; j++)
149 {
150 cost += next_delete_cost[j];
151 matrix[j].deletecost = cost;
152 matrix[j].writecost = INFINITY;
153 matrix[j].insertcost = INFINITY;
154 matrix[j].deletecount = j;
155 matrix[j].insertcount = 0;
156 }
157
0137dbf7
JB
158 /* `i' represents the vpos among new frame contents.
159 `j' represents the vpos among the old frame contents. */
632a8dd6
JB
160 p = matrix + window_size + 2; /* matrix [1, 1] */
161 for (i = 1; i <= window_size; i++, p++)
162 for (j = 1; j <= window_size; j++, p++)
163 {
164 /* p contains the address of matrix [i, j] */
165
166 /* First calculate the cost assuming we do
167 not insert or delete above this line.
168 That is, if we update through line i-1
169 based on old lines through j-1,
170 and then just change old line j to new line i. */
171 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
172 cost = p1->writecost;
173 if (cost > p1->insertcost)
174 cost = p1->insertcost;
175 if (cost > p1->deletecost)
176 cost = p1->deletecost;
177 if (old_hash[j] != new_hash[i])
178 cost += draw_cost[i];
179 p->writecost = cost;
180
181 /* Calculate the cost if we do an insert-line
182 before outputting this line.
183 That is, we update through line i-1
184 based on old lines through j,
185 do an insert-line on line i,
186 and then output line i from scratch,
187 leaving old lines starting from j for reuse below. */
188 p1 = p - window_size - 1; /* matrix [i-1, j] */
189 /* No need to think about doing a delete followed
190 immediately by an insert. It cannot be as good
191 as not doing either of them. */
192 if (free_at_end == i)
193 {
194 cost = p1->writecost;
195 cost1 = p1->insertcost;
196 }
197 else
198 {
199 cost = p1->writecost + first_insert_cost[i];
0fe4741d 200 if ((int) p1->insertcount > i)
632a8dd6
JB
201 abort ();
202 cost1 = p1->insertcost + next_insert_cost[i - p1->insertcount];
203 }
204 p->insertcost = min (cost, cost1) + draw_cost[i] + extra_cost;
205 p->insertcount = (cost < cost1) ? 1 : p1->insertcount + 1;
0fe4741d 206 if ((int) p->insertcount > i)
632a8dd6
JB
207 abort ();
208
209 /* Calculate the cost if we do a delete line after
210 outputting this line.
211 That is, we update through line i
212 based on old lines through j-1,
213 and throw away old line j. */
214 p1 = p - 1; /* matrix [i, j-1] */
215 /* No need to think about doing an insert followed
216 immediately by a delete. */
217 if (free_at_end == i)
218 {
219 cost = p1->writecost;
220 cost1 = p1->deletecost;
221 }
222 else
223 {
224 cost = p1->writecost + first_delete_cost[i];
225 cost1 = p1->deletecost + next_delete_cost[i];
226 }
227 p->deletecost = min (cost, cost1);
228 p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1;
229 }
230}
89700111
GM
231
232
632a8dd6 233\f
89700111
GM
234/* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
235 according to the costs in MATRIX, using the general scrolling
236 method that is used if the terminal does not support the setting of
177c0ea7 237 scroll windows (scroll_region_ok == 0).
2d810f84
RS
238
239 WINDOW_SIZE is the number of lines being considered for scrolling
89700111
GM
240 and UNCHANGED_AT_TOP is the vpos of the first line being
241 considered. These two arguments can specify any contiguous range
242 of lines. */
177c0ea7 243
632a8dd6 244static void
89700111
GM
245do_scrolling (current_matrix, matrix, window_size, unchanged_at_top)
246 struct glyph_matrix *current_matrix;
632a8dd6
JB
247 struct matrix_elt *matrix;
248 int window_size;
249 int unchanged_at_top;
250{
89700111
GM
251 struct matrix_elt *p;
252 int i, j, k;
253
254 /* Set to 1 if we have set a terminal window with
255 set_terminal_window. */
256 int terminal_window_p = 0;
257
258 /* A queue for line insertions to be done. */
259 struct queue { int count, pos; };
260 struct queue *queue_start
261 = (struct queue *) alloca (current_matrix->nrows * sizeof (struct queue));
262 struct queue *queue = queue_start;
177c0ea7 263
89700111
GM
264 char *retained_p = (char *) alloca (window_size * sizeof (char));
265 int *copy_from = (int *) alloca (window_size * sizeof (int));
266
267 /* Zero means line is empty. */
268 bzero (retained_p, window_size * sizeof (char));
269 for (k = 0; k < window_size; ++k)
270 copy_from[k] = -1;
271
b96f9fb7 272#define CHECK_BOUNDS \
89700111
GM
273 do \
274 { \
275 int k; \
276 for (k = 0; k < window_size; ++k) \
277 xassert (copy_from[k] == -1 \
278 || (copy_from[k] >= 0 && copy_from[k] < window_size)); \
279 } \
280 while (0);
281
282 /* When j is advanced, this corresponds to deleted lines.
283 When i is advanced, this corresponds to inserted lines. */
632a8dd6 284 i = j = window_size;
632a8dd6
JB
285 while (i > 0 || j > 0)
286 {
287 p = matrix + i * (window_size + 1) + j;
177c0ea7 288
89700111 289 if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
632a8dd6 290 {
89700111
GM
291 /* Insert should be done at vpos i-1, plus maybe some before.
292 Queue the screen operation to be performed. */
293 queue->count = p->insertcount;
294 queue->pos = i + unchanged_at_top - p->insertcount;
295 ++queue;
296
297 /* By incrementing I, we leave room in the result rows
298 for the empty rows opened up. */
632a8dd6 299 i -= p->insertcount;
632a8dd6
JB
300 }
301 else if (p->deletecost < p->writecost)
302 {
89700111
GM
303 /* Old line at vpos j-1, and maybe some before it, should be
304 deleted. By decrementing J, we skip some lines in the
305 temp_rows which is equivalent to omitting these lines in
306 the result rows, thus deleting them. */
632a8dd6 307 j -= p->deletecount;
89700111
GM
308
309 /* Set the terminal window, if not done already. */
310 if (! terminal_window_p)
632a8dd6
JB
311 {
312 set_terminal_window (window_size + unchanged_at_top);
89700111 313 terminal_window_p = 1;
632a8dd6 314 }
89700111
GM
315
316 /* Delete lines on the terminal. */
632a8dd6
JB
317 ins_del_lines (j + unchanged_at_top, - p->deletecount);
318 }
319 else
320 {
89700111
GM
321 /* Best thing done here is no insert or delete, i.e. a write. */
322 --i, --j;
323 xassert (i >= 0 && i < window_size);
324 xassert (j >= 0 && j < window_size);
325 copy_from[i] = j;
326 retained_p[j] = 1;
327
328#if GLYPH_DEBUG
b96f9fb7 329 CHECK_BOUNDS;
89700111 330#endif
632a8dd6
JB
331 }
332 }
333
89700111
GM
334 /* Now do all insertions queued above. */
335 if (queue > queue_start)
632a8dd6 336 {
89700111 337 int next = -1;
632a8dd6 338
89700111
GM
339 /* Set the terminal window if not yet done. */
340 if (!terminal_window_p)
341 {
342 set_terminal_window (window_size + unchanged_at_top);
343 terminal_window_p = 1;
344 }
632a8dd6 345
89700111 346 do
632a8dd6 347 {
89700111
GM
348 --queue;
349
350 /* Do the deletion on the terminal. */
351 ins_del_lines (queue->pos, queue->count);
352
353 /* All lines in the range deleted become empty in the glyph
354 matrix. Assign to them glyph rows that are not retained.
355 K is the starting position of the deleted range relative
356 to the window we are working in. */
357 k = queue->pos - unchanged_at_top;
358 for (j = 0; j < queue->count; ++j)
359 {
360 /* Find the next row not retained. */
361 while (retained_p[++next])
362 ;
363
364 /* Record that this row is to be used for the empty
365 glyph row j. */
366 copy_from[k + j] = next;
367 }
632a8dd6 368 }
89700111 369 while (queue > queue_start);
177c0ea7 370
632a8dd6
JB
371 }
372
89700111
GM
373 for (k = 0; k < window_size; ++k)
374 xassert (copy_from[k] >= 0 && copy_from[k] < window_size);
375
376 /* Perform the row swizzling. */
377 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
378 copy_from, retained_p);
379
380 /* Some sanity checks if GLYPH_DEBUG != 0. */
381 CHECK_MATRIX (current_matrix);
177c0ea7 382
89700111 383 if (terminal_window_p)
632a8dd6
JB
384 set_terminal_window (0);
385}
89700111 386
632a8dd6 387\f
1af1b1e8
RS
388/* Determine, in matrix[i,j], the cost of updating the first j
389 old lines into the first i new lines using the direct
390 scrolling method. When the old line and the new line have
391 different hash codes, the calculated cost of updating old
392 line j into new line i includes the cost of outputting new
393 line i, and if i != j, the cost of outputting the old line j
394 is also included, as a penalty for moving the line and then
395 erasing it. In addition, the cost of updating a sequence of
396 lines with constant i - j includes the cost of scrolling the
397 old lines into their new positions, unless i == j. Scrolling
398 is achieved by setting the screen window to avoid affecting
399 other lines below, and inserting or deleting lines at the top
400 of the scrolled region. The cost of scrolling a sequence of
401 lines includes the fixed cost of specifying a scroll region,
402 plus a variable cost which can depend upon the number of lines
403 involved and the distance by which they are scrolled, and an
404 extra cost to discourage long scrolls.
405
406 As reflected in the matrix, an insert or delete does not
407 correspond directly to the insertion or deletion which is
408 used in scrolling lines. An insert means that the value of i
409 has increased without a corresponding increase in the value
410 of j. A delete means that the value of j has increased
411 without a corresponding increase in the value of i. A write
412 means that i and j are both increased by the same amount, and
413 that the old lines will be moved to their new positions.
414
415 An insert following a delete is allowed only if i > j.
416 A delete following an insert is allowed only if i < j.
417 These restrictions ensure that the new lines in an insert
418 will always be blank as an effect of the neighboring writes.
419 Thus the calculated cost of an insert is simply the cost of
420 outputting the new line contents. The direct cost of a
421 delete is zero. Inserts and deletes indirectly affect the
422 total cost through their influence on subsequent writes. */
423
424/* The vectors draw_cost, old_hash, and new_hash have the same
425 meanings here as in calculate_scrolling, and old_draw_cost
426 is the equivalent of draw_cost for the old line contents */
427
428static void
429calculate_direct_scrolling (frame, matrix, window_size, lines_below,
430 draw_cost, old_draw_cost, old_hash, new_hash,
431 free_at_end)
432 FRAME_PTR frame;
433 /* matrix is of size window_size + 1 on each side. */
434 struct matrix_elt *matrix;
250368af 435 int window_size, lines_below;
1af1b1e8
RS
436 int *draw_cost;
437 int *old_draw_cost;
438 int *old_hash;
439 int *new_hash;
440 int free_at_end;
441{
442 register int i, j;
9882535b 443 int frame_lines = FRAME_LINES (frame);
1af1b1e8
RS
444 register struct matrix_elt *p, *p1;
445 register int cost, cost1, delta;
446
447 /* first_insert_cost[-I] is the cost of doing the first insert-line
448 at a position I lines above the bottom line in the scroll window. */
449 int *first_insert_cost
9882535b 450 = &FRAME_INSERT_COST (frame)[frame_lines - 1];
1af1b1e8 451 int *first_delete_cost
9882535b 452 = &FRAME_DELETE_COST (frame)[frame_lines - 1];
1af1b1e8 453 int *next_insert_cost
9882535b 454 = &FRAME_INSERTN_COST (frame)[frame_lines - 1];
1af1b1e8 455 int *next_delete_cost
9882535b 456 = &FRAME_DELETEN_COST (frame)[frame_lines - 1];
1af1b1e8
RS
457
458 int scroll_overhead;
459
460 /* Discourage long scrolls on fast lines.
461 Don't scroll nearly a full frame height unless it saves
462 at least 1/4 second. */
9882535b 463 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
1af1b1e8
RS
464
465 if (baud_rate <= 0)
466 extra_cost = 1;
467
468 /* Overhead of setting the scroll window, plus the extra cost
469 cost of scrolling by a distance of one. The extra cost is
470 added once for consistency with the cost vectors */
8a56675d 471 scroll_overhead
daf01701 472 = FRAME_SCROLL_REGION_COST (frame) + extra_cost;
1af1b1e8
RS
473
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;
481
482 /* initialize the left edge of the matrix */
483 cost = 0;
484 for (i = 1; i <= window_size; i++)
485 {
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;
494 }
495
496 /* initialize the top edge of the matrix */
497 for (j = 1; j <= window_size; j++)
498 {
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;
505 }
506
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] */
510
511 for (i = 1; i <= window_size; i++, p++)
512 for (j = 1; j <= window_size; j++, p++)
513 {
514 /* p contains the address of matrix [i, j] */
515
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.
521
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)
532 {
533 if (cost > cost1)
534 {
535 cost = cost1;
536 p->writecount = p1->writecount + 1;
537 }
538 else
539 p->writecount = 1;
540 if (old_hash[j] != new_hash[i])
541 {
542 cost += draw_cost[i];
543 }
544 }
545 else
546 {
547 if (i > j)
548 {
549 delta = i - j;
550
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);
558
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] +
177c0ea7 568 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
1af1b1e8
RS
569 }
570 else
571 {
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] +
177c0ea7 576 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
1af1b1e8
RS
577 }
578 if (cost1 < cost)
579 {
580 cost = cost1;
581 p->writecount = p1->writecount + 1;
582 }
583 else
584 p->writecount = 1;
585 if (old_hash[j] != new_hash[i])
586 {
587 cost += draw_cost[i] + old_draw_cost[j];
588 }
589 }
590 p->writecost = cost;
591
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)
605 {
606 cost = p1->insertcost;
607 p->insertcount = p1->insertcount + 1;
608 }
609 else
610 p->insertcount = 1;
611 cost += draw_cost[i];
612 p->insertcost = cost;
613
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)
626 {
627 cost = p1->deletecost;
628 p->deletecount = p1->deletecount + 1;
629 }
630 else
631 p->deletecount = 1;
632 p->deletecost = cost;
633 }
634}
89700111
GM
635
636
1af1b1e8 637\f
89700111
GM
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).
1af1b1e8
RS
642
643 WINDOW_SIZE is the number of lines being considered for scrolling
89700111
GM
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.
177c0ea7 647
1af1b1e8
RS
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
89700111
GM
651 is described in more detail in calculate_direct_scrolling, where
652 the cost matrix for this approach is constructed. */
1af1b1e8
RS
653
654static void
89700111
GM
655do_direct_scrolling (current_matrix, cost_matrix, window_size,
656 unchanged_at_top)
657 struct glyph_matrix *current_matrix;
658 struct matrix_elt *cost_matrix;
1af1b1e8
RS
659 int window_size;
660 int unchanged_at_top;
661{
89700111
GM
662 struct matrix_elt *p;
663 int i, j;
1af1b1e8 664
89700111
GM
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;
1af1b1e8 670
89700111
GM
671 /* Set to 1 if a terminal window has been set with
672 set_terminal_window: */
673 int terminal_window_p = 0;
1af1b1e8 674
89700111
GM
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;
683
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));
686
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));
690
691 bzero (retained_p, window_size * sizeof (char));
692
693 /* Perform some sanity checks when GLYPH_DEBUG is on. */
694 CHECK_MATRIX (current_matrix);
695
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.
701
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;
177c0ea7 707
1af1b1e8
RS
708 while (i > 0 || j > 0)
709 {
89700111 710 p = cost_matrix + i * (window_size + 1) + j;
177c0ea7 711
89700111
GM
712 if (p->insertcost < p->writecost
713 && p->insertcost < p->deletecost
714 && (write_follows_p || i < j))
1af1b1e8 715 {
89700111
GM
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;
177c0ea7 723
1af1b1e8 724 i -= p->insertcount;
89700111 725 write_follows_p = 0;
1af1b1e8 726 }
89700111
GM
727 else if (p->deletecost < p->writecost
728 && (write_follows_p || i > j))
1af1b1e8 729 {
89700111
GM
730 /* Deleting lines is cheaper. By decrementing J, omit
731 deletecount lines from the original. */
732 write_follows_p = 0;
1af1b1e8
RS
733 j -= p->deletecount;
734 }
735 else
736 {
89700111
GM
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;
177c0ea7 742 xassert (n_to_write > 0);
89700111 743
1af1b1e8
RS
744 if (i > j)
745 {
89700111 746 /* Immediately insert lines */
1af1b1e8 747 set_terminal_window (i + unchanged_at_top);
89700111
GM
748 terminal_window_p = 1;
749 ins_del_lines (j - n_to_write + unchanged_at_top, i - j);
1af1b1e8
RS
750 }
751 else if (i < j)
752 {
89700111
GM
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;
1af1b1e8
RS
758 }
759
89700111 760 while (n_to_write > 0)
1af1b1e8 761 {
89700111
GM
762 --i, --j, --n_to_write;
763 copy_from[i] = j;
764 retained_p[j] = 1;
1af1b1e8
RS
765 }
766 }
767 }
768
89700111
GM
769 /* Do queued operations. */
770 if (queue > queue_start)
1af1b1e8 771 {
89700111
GM
772 int next = -1;
773
774 do
1af1b1e8 775 {
89700111
GM
776 --queue;
777 if (queue->count)
778 {
779 set_terminal_window (queue->window);
780 terminal_window_p = 1;
781 ins_del_lines (queue->pos, queue->count);
782 }
783 else
1af1b1e8 784 {
89700111
GM
785 for (j = queue->window - 1; j >= queue->pos; --j)
786 {
787 while (retained_p[++next])
788 ;
789 copy_from[j] = next;
790 }
1af1b1e8
RS
791 }
792 }
89700111 793 while (queue > queue_start);
1af1b1e8
RS
794 }
795
89700111
GM
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);
802
803 if (terminal_window_p)
1af1b1e8
RS
804 set_terminal_window (0);
805}
89700111
GM
806
807
1af1b1e8 808\f
632a8dd6 809void
0137dbf7 810scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
1af1b1e8 811 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
0137dbf7 812 FRAME_PTR frame;
632a8dd6
JB
813 int window_size, unchanged_at_top, unchanged_at_bottom;
814 int *draw_cost;
1af1b1e8 815 int *old_draw_cost;
632a8dd6
JB
816 int *old_hash;
817 int *new_hash;
818 int free_at_end;
819{
820 struct matrix_elt *matrix;
821 matrix = ((struct matrix_elt *)
822 alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
823
daf01701 824 if (FRAME_SCROLL_REGION_OK (frame))
1af1b1e8
RS
825 {
826 calculate_direct_scrolling (frame, matrix, window_size,
827 unchanged_at_bottom,
177c0ea7 828 draw_cost, old_draw_cost,
1af1b1e8 829 old_hash, new_hash, free_at_end);
89700111
GM
830 do_direct_scrolling (frame->current_matrix,
831 matrix, window_size, unchanged_at_top);
1af1b1e8
RS
832 }
833 else
834 {
835 calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
836 draw_cost, old_hash, new_hash,
837 free_at_end);
89700111
GM
838 do_scrolling (frame->current_matrix, matrix, window_size,
839 unchanged_at_top);
1af1b1e8 840 }
632a8dd6 841}
89700111
GM
842
843
632a8dd6 844\f
89700111
GM
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. */
632a8dd6
JB
850
851int
852scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
853 int start, end;
854 int *oldhash, *newhash, *cost;
855{
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;
861
862 /* Compute a threshold which is 1/4 of average length of these lines. */
863
864 for (i = start; i < end; i++)
865 avg_length += cost[i];
866
867 avg_length /= end - start;
868 threshold = avg_length / 4;
869
870 bzero (lines, sizeof lines);
871
89700111
GM
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. */
632a8dd6
JB
875 for (i = start; i < end; i++)
876 {
877 if (cost[i] > threshold)
878 {
879 h = newhash[i] & 0777;
880 lines[h].hash = newhash[i];
881 lines[h].count++;
882 }
883 }
884
89700111
GM
885 /* Look up old line hash codes in the hash table. Count number of
886 matches between old lines and new. */
632a8dd6
JB
887 for (i = start; i < end; i++)
888 {
889 h = oldhash[i] & 0777;
890 if (oldhash[i] == lines[h].hash)
891 {
892 matchcount++;
893 if (--lines[h].count == 0)
894 lines[h].hash = 0;
895 }
896 }
897
898 return matchcount;
899}
900\f
89700111
GM
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. */
632a8dd6 905
dfcf069d 906int
0137dbf7
JB
907scroll_cost (frame, from, to, amount)
908 FRAME_PTR frame;
632a8dd6
JB
909 int from, to, amount;
910{
0137dbf7 911 /* Compute how many lines, at bottom of frame,
632a8dd6
JB
912 will not be involved in actual motion. */
913 int limit = to;
914 int offset;
9882535b 915 int height = FRAME_LINES (frame);
632a8dd6 916
632a8dd6
JB
917 if (amount == 0)
918 return 0;
919
daf01701 920 if (! FRAME_SCROLL_REGION_OK (frame))
3e2bea7c
JB
921 limit = height;
922 else if (amount > 0)
923 limit += amount;
924
632a8dd6
JB
925 if (amount < 0)
926 {
927 int temp = to;
928 to = from + amount;
929 from = temp + amount;
930 amount = - amount;
931 }
932
933 offset = height - limit;
934
935 return
0137dbf7
JB
936 (FRAME_INSERT_COST (frame)[offset + from]
937 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
00eb4c4a
RS
938 + FRAME_DELETE_COST (frame)[offset + to]
939 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
632a8dd6
JB
940}
941\f
942/* Calculate the line insertion/deletion
943 overhead and multiply factor values */
944
945static void
0137dbf7
JB
946line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
947 FRAME_PTR frame;
632a8dd6
JB
948 int ov1, ovn;
949 int pf1, pfn;
950 register int *ov, *mf;
951{
952 register int i;
9882535b 953 register int frame_lines = FRAME_LINES (frame);
632a8dd6
JB
954 register int insert_overhead = ov1 * 10;
955 register int next_insert_cost = ovn * 10;
956
9882535b 957 for (i = frame_lines-1; i >= 0; i--)
632a8dd6 958 {
326e0cf8 959 mf[i] = next_insert_cost / 10;
632a8dd6 960 next_insert_cost += pfn;
326e0cf8 961 ov[i] = (insert_overhead + next_insert_cost) / 10;
632a8dd6
JB
962 insert_overhead += pf1;
963 }
964}
965
966static void
0137dbf7 967ins_del_costs (frame,
632a8dd6
JB
968 one_line_string, multi_string,
969 setup_string, cleanup_string,
970 costvec, ncostvec, coefficient)
0137dbf7 971 FRAME_PTR frame;
632a8dd6
JB
972 char *one_line_string, *multi_string;
973 char *setup_string, *cleanup_string;
974 int *costvec, *ncostvec;
975 int coefficient;
976{
977 if (multi_string)
0137dbf7 978 line_ins_del (frame,
632a8dd6
JB
979 string_cost (multi_string) * coefficient,
980 per_line_cost (multi_string) * coefficient,
981 0, 0, costvec, ncostvec);
982 else if (one_line_string)
0137dbf7 983 line_ins_del (frame,
632a8dd6
JB
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
0137dbf7 989 line_ins_del (frame,
632a8dd6
JB
990 9999, 0, 9999, 0,
991 costvec, ncostvec);
992}
993
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).
0137dbf7 997 This must be redone whenever the frame height changes.
632a8dd6
JB
998
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.
1004
1005 The cost to insert N lines at line L is
9882535b
KS
1006 [tt.t_ILov + (frame_lines + 1 - L) * tt.t_ILpf] +
1007 N * [tt.t_ILnov + (frame_lines + 1 - L) * tt.t_ILnpf]
632a8dd6
JB
1008
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
9882535b 1011 L changes (frame_lines + 1 - L) lines.
632a8dd6
JB
1012
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
0137dbf7
JB
1016 FRAME_INSERT_COST (frame) and the multiply factor in
1017 FRAME_INSERTN_COST (frame). Note however that any insertion
632a8dd6 1018 must include at least one multiply factor. Rather than compute this
0137dbf7
JB
1019 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1020 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
632a8dd6
JB
1021 This is reasonable because of the particular algorithm used in calcM.
1022
1023 Deletion is essentially the same as insertion.
1024 */
1025
dfcf069d 1026void
0137dbf7 1027do_line_insertion_deletion_costs (frame,
632a8dd6
JB
1028 ins_line_string, multi_ins_string,
1029 del_line_string, multi_del_string,
1030 setup_string, cleanup_string, coefficient)
0137dbf7 1031 FRAME_PTR frame;
632a8dd6
JB
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;
1036{
0137dbf7 1037 if (FRAME_INSERT_COST (frame) != 0)
632a8dd6 1038 {
0137dbf7
JB
1039 FRAME_INSERT_COST (frame) =
1040 (int *) xrealloc (FRAME_INSERT_COST (frame),
9882535b 1041 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1042 FRAME_DELETEN_COST (frame) =
1043 (int *) xrealloc (FRAME_DELETEN_COST (frame),
9882535b 1044 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1045 FRAME_INSERTN_COST (frame) =
1046 (int *) xrealloc (FRAME_INSERTN_COST (frame),
9882535b 1047 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1048 FRAME_DELETE_COST (frame) =
1049 (int *) xrealloc (FRAME_DELETE_COST (frame),
9882535b 1050 FRAME_LINES (frame) * sizeof (int));
632a8dd6
JB
1051 }
1052 else
1053 {
0137dbf7 1054 FRAME_INSERT_COST (frame) =
9882535b 1055 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
0137dbf7 1056 FRAME_DELETEN_COST (frame) =
9882535b 1057 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
0137dbf7 1058 FRAME_INSERTN_COST (frame) =
9882535b 1059 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
177c0ea7 1060 FRAME_DELETE_COST (frame) =
9882535b 1061 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
632a8dd6
JB
1062 }
1063
0137dbf7 1064 ins_del_costs (frame,
632a8dd6
JB
1065 ins_line_string, multi_ins_string,
1066 setup_string, cleanup_string,
0137dbf7 1067 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
632a8dd6 1068 coefficient);
0137dbf7 1069 ins_del_costs (frame,
632a8dd6
JB
1070 del_line_string, multi_del_string,
1071 setup_string, cleanup_string,
fd374ddc 1072 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
632a8dd6
JB
1073 coefficient);
1074}
ab5796a9
MB
1075
1076/* arch-tag: cdb7149c-48e7-4793-a948-2786c8e45485
1077 (do not change this comment) */