Include keyboard.h before frame.h.
[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>
632a8dd6
JB
25#include "termchar.h"
26#include "lisp.h"
27#include "dispextern.h"
0137dbf7 28#include "frame.h"
89700111 29#include "window.h"
632a8dd6
JB
30
31#define max(a, b) ((a) > (b) ? (a) : (b))
32#define min(a, b) ((a) < (b) ? (a) : (b))
33
34/* All costs measured in characters.
0137dbf7 35 So no cost can exceed the area of a frame, measured in characters.
61df1a5e 36 Let's hope this is never more than 1000000 characters. */
632a8dd6 37
61df1a5e 38#define INFINITY 1000000
632a8dd6
JB
39
40struct matrix_elt
41 {
42 /* Cost of outputting through this line
43 if no insert/delete is done just above it. */
61df1a5e 44 int writecost;
632a8dd6
JB
45 /* Cost of outputting through this line
46 if an insert is done just above it. */
61df1a5e 47 int insertcost;
632a8dd6
JB
48 /* Cost of outputting through this line
49 if a delete is done just above it. */
61df1a5e 50 int deletecost;
632a8dd6
JB
51 /* Number of inserts so far in this run of inserts,
52 for the cost in insertcost. */
61df1a5e 53 unsigned char insertcount;
632a8dd6
JB
54 /* Number of deletes so far in this run of deletes,
55 for the cost in deletecost. */
61df1a5e 56 unsigned char deletecount;
1af1b1e8
RS
57 /* Number of writes so far since the last insert
58 or delete for the cost in writecost. */
59 unsigned char writecount;
632a8dd6
JB
60 };
61
89700111
GM
62static void do_direct_scrolling P_ ((struct glyph_matrix *,
63 struct matrix_elt *,
64 int, int));
65static void do_scrolling P_ ((struct glyph_matrix *,
66 struct matrix_elt *,
67 int, int));
68
632a8dd6 69\f
1af1b1e8
RS
70/* Determine, in matrix[i,j], the cost of updating the first j old
71 lines into the first i new lines using the general scrolling method.
632a8dd6
JB
72 This involves using insert or delete somewhere if i != j.
73 For each matrix elements, three kinds of costs are recorded:
74 the smallest cost that ends with an insert, the smallest
75 cost that ends with a delete, and the smallest cost that
76 ends with neither one. These are kept separate because
77 on some terminals the cost of doing an insert varies
78 depending on whether one was just done, etc. */
79
80/* draw_cost[VPOS] is the cost of outputting new line at VPOS.
81 old_hash[VPOS] is the hash code of the old line at VPOS.
82 new_hash[VPOS] is the hash code of the new line at VPOS.
0137dbf7 83 Note that these are not true frame vpos's, but relative
632a8dd6
JB
84 to the place at which the first mismatch between old and
85 new contents appears. */
86
87static void
0137dbf7 88calculate_scrolling (frame, matrix, window_size, lines_below,
632a8dd6
JB
89 draw_cost, old_hash, new_hash,
90 free_at_end)
0137dbf7 91 FRAME_PTR frame;
632a8dd6
JB
92 /* matrix is of size window_size + 1 on each side. */
93 struct matrix_elt *matrix;
94 int window_size;
95 int *draw_cost;
96 int *old_hash;
97 int *new_hash;
98 int free_at_end;
99{
100 register int i, j;
0137dbf7 101 int frame_height = FRAME_HEIGHT (frame);
632a8dd6
JB
102 register struct matrix_elt *p, *p1;
103 register int cost, cost1;
104
105 int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below);
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
0137dbf7 110 = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved];
632a8dd6 111 int *first_delete_cost
0137dbf7 112 = &FRAME_DELETE_COST (frame)[frame_height - 1 - lines_moved];
632a8dd6 113 int *next_insert_cost
0137dbf7 114 = &FRAME_INSERTN_COST (frame)[frame_height - 1 - lines_moved];
632a8dd6 115 int *next_delete_cost
0137dbf7 116 = &FRAME_DELETEN_COST (frame)[frame_height - 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. */
0137dbf7 121 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (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
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. */
2d810f84 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;
263
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;
89700111
GM
288
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
GM
369 while (queue > queue_start);
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);
382
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;
435 int window_size;
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;
443 int frame_height = FRAME_HEIGHT (frame);
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
450 = &FRAME_INSERT_COST (frame)[frame_height - 1];
451 int *first_delete_cost
452 = &FRAME_DELETE_COST (frame)[frame_height - 1];
453 int *next_insert_cost
454 = &FRAME_INSERTN_COST (frame)[frame_height - 1];
455 int *next_delete_cost
456 = &FRAME_DELETEN_COST (frame)[frame_height - 1];
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. */
463 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
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 */
471 scroll_overhead = scroll_region_cost + extra_cost;
472
473 /* initialize the top left corner of the matrix */
474 matrix->writecost = 0;
475 matrix->insertcost = INFINITY;
476 matrix->deletecost = INFINITY;
477 matrix->writecount = 0;
478 matrix->insertcount = 0;
479 matrix->deletecount = 0;
480
481 /* initialize the left edge of the matrix */
482 cost = 0;
483 for (i = 1; i <= window_size; i++)
484 {
485 p = matrix + i * (window_size + 1);
486 cost += draw_cost[i];
487 p->insertcost = cost;
488 p->writecost = INFINITY;
489 p->deletecost = INFINITY;
490 p->insertcount = i;
491 p->writecount = 0;
492 p->deletecount = 0;
493 }
494
495 /* initialize the top edge of the matrix */
496 for (j = 1; j <= window_size; j++)
497 {
498 matrix[j].deletecost = 0;
499 matrix[j].writecost = INFINITY;
500 matrix[j].insertcost = INFINITY;
501 matrix[j].deletecount = j;
502 matrix[j].writecount = 0;
503 matrix[j].insertcount = 0;
504 }
505
506 /* `i' represents the vpos among new frame contents.
507 `j' represents the vpos among the old frame contents. */
508 p = matrix + window_size + 2; /* matrix [1, 1] */
509
510 for (i = 1; i <= window_size; i++, p++)
511 for (j = 1; j <= window_size; j++, p++)
512 {
513 /* p contains the address of matrix [i, j] */
514
515 /* First calculate the cost assuming we do
516 not insert or delete above this line.
517 That is, if we update through line i-1
518 based on old lines through j-1,
519 and then just change old line j to new line i.
520
521 Depending on which choice gives the lower cost,
522 this usually involves either scrolling a single line
523 or extending a sequence of scrolled lines, but
524 when i == j, no scrolling is required. */
525 p1 = p - window_size - 2; /* matrix [i-1, j-1] */
526 cost = p1->insertcost;
527 if (cost > p1->deletecost)
528 cost = p1->deletecost;
529 cost1 = p1->writecost;
530 if (i == j)
531 {
532 if (cost > cost1)
533 {
534 cost = cost1;
535 p->writecount = p1->writecount + 1;
536 }
537 else
538 p->writecount = 1;
539 if (old_hash[j] != new_hash[i])
540 {
541 cost += draw_cost[i];
542 }
543 }
544 else
545 {
546 if (i > j)
547 {
548 delta = i - j;
549
550 /* The cost added here for scrolling the first line by
551 a distance N includes the overhead of setting the
552 scroll window, the cost of inserting N lines at a
553 position N lines above the bottom line of the window,
554 and an extra cost which is proportional to N. */
555 cost += scroll_overhead + first_insert_cost[-delta] +
556 (delta-1) * (next_insert_cost[-delta] + extra_cost);
557
558 /* In the most general case, the insertion overhead and
559 the multiply factor can grow linearly as the distance
560 from the bottom of the window increases. The incremental
561 cost of scrolling an additional line depends upon the
562 rate of change of these two parameters. Each of these
563 growth rates can be determined by a simple difference.
564 To reduce the cumulative effects of rounding error, we
565 vary the position at which the difference is computed. */
566 cost1 += first_insert_cost[-j] - first_insert_cost[1-j] +
567 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
568 }
569 else
570 {
571 delta = j - i;
572 cost += scroll_overhead + first_delete_cost[-delta] +
573 (delta-1) * (next_delete_cost[-delta] + extra_cost);
574 cost1 += first_delete_cost[-i] - first_delete_cost[1-i] +
575 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
576 }
577 if (cost1 < cost)
578 {
579 cost = cost1;
580 p->writecount = p1->writecount + 1;
581 }
582 else
583 p->writecount = 1;
584 if (old_hash[j] != new_hash[i])
585 {
586 cost += draw_cost[i] + old_draw_cost[j];
587 }
588 }
589 p->writecost = cost;
590
591 /* Calculate the cost if we do an insert-line
592 before outputting this line.
593 That is, we update through line i-1
594 based on old lines through j,
595 do an insert-line on line i,
596 and then output line i from scratch,
597 leaving old lines starting from j for reuse below. */
598 p1 = p - window_size - 1; /* matrix [i-1, j] */
599 cost = p1->writecost;
600 /* If i > j, an insert is allowed after a delete. */
601 if (i > j && p1->deletecost < cost)
602 cost = p1->deletecost;
603 if (p1->insertcost <= cost)
604 {
605 cost = p1->insertcost;
606 p->insertcount = p1->insertcount + 1;
607 }
608 else
609 p->insertcount = 1;
610 cost += draw_cost[i];
611 p->insertcost = cost;
612
613 /* Calculate the cost if we do a delete line after
614 outputting this line.
615 That is, we update through line i
616 based on old lines through j-1,
617 and throw away old line j. */
618 p1 = p - 1; /* matrix [i, j-1] */
619 cost = p1->writecost;
620 /* If i < j, a delete is allowed after an insert. */
621 if (i < j && p1->insertcost < cost)
622 cost = p1->insertcost;
623 cost1 = p1->deletecost;
624 if (p1->deletecost <= cost)
625 {
626 cost = p1->deletecost;
627 p->deletecount = p1->deletecount + 1;
628 }
629 else
630 p->deletecount = 1;
631 p->deletecost = cost;
632 }
633}
89700111
GM
634
635
1af1b1e8 636\f
89700111
GM
637/* Perform insert-lines and delete-lines operations on CURRENT_MATRIX
638 according to the costs in MATRIX, using the direct scrolling method
639 which is used when the terminal supports setting a scroll window
640 (scroll_region_ok).
1af1b1e8
RS
641
642 WINDOW_SIZE is the number of lines being considered for scrolling
89700111
GM
643 and UNCHANGED_AT_TOP is the vpos of the first line being
644 considered. These two arguments can specify any contiguous range
645 of lines.
1af1b1e8 646
1af1b1e8
RS
647 In the direct scrolling method, a new scroll window is selected
648 before each insertion or deletion, so that groups of lines can be
649 scrolled directly to their final vertical positions. This method
89700111
GM
650 is described in more detail in calculate_direct_scrolling, where
651 the cost matrix for this approach is constructed. */
1af1b1e8
RS
652
653static void
89700111
GM
654do_direct_scrolling (current_matrix, cost_matrix, window_size,
655 unchanged_at_top)
656 struct glyph_matrix *current_matrix;
657 struct matrix_elt *cost_matrix;
1af1b1e8
RS
658 int window_size;
659 int unchanged_at_top;
660{
89700111
GM
661 struct matrix_elt *p;
662 int i, j;
1af1b1e8 663
89700111
GM
664 /* A queue of deletions and insertions to be performed. */
665 struct alt_queue { int count, pos, window; };
666 struct alt_queue *queue_start = (struct alt_queue *)
667 alloca (window_size * sizeof *queue_start);
668 struct alt_queue *queue = queue_start;
1af1b1e8 669
89700111
GM
670 /* Set to 1 if a terminal window has been set with
671 set_terminal_window: */
672 int terminal_window_p = 0;
1af1b1e8 673
89700111
GM
674 /* A nonzero value of write_follows indicates that a write has been
675 selected, allowing either an insert or a delete to be selected
676 next. When write_follows is zero, a delete cannot be selected
677 unless j < i, and an insert cannot be selected unless i < j.
678 This corresponds to a similar restriction (with the ordering
679 reversed) in calculate_direct_scrolling, which is intended to
680 ensure that lines marked as inserted will be blank. */
681 int write_follows_p = 1;
682
683 /* For each row in the new matrix what row of the old matrix it is. */
684 int *copy_from = (int *) alloca (window_size * sizeof (int));
685
686 /* Non-zero for each row in the new matrix that is retained from the
687 old matrix. Lines not retained are empty. */
688 char *retained_p = (char *) alloca (window_size * sizeof (char));
689
690 bzero (retained_p, window_size * sizeof (char));
691
692 /* Perform some sanity checks when GLYPH_DEBUG is on. */
693 CHECK_MATRIX (current_matrix);
694
695 /* We are working on the line range UNCHANGED_AT_TOP ...
696 UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX.
697 We step through lines in this range from the end to the start. I
698 is an index into new lines, j an index into old lines. The cost
699 matrix determines what to do for ranges of indices.
700
701 If i is decremented without also decrementing j, this corresponds
702 to inserting empty lines in the result. If j is decremented
703 without also decrementing i, this corresponds to omitting these
704 lines in the new rows, i.e. rows are deleted. */
705 i = j = window_size;
706
1af1b1e8
RS
707 while (i > 0 || j > 0)
708 {
89700111
GM
709 p = cost_matrix + i * (window_size + 1) + j;
710
711 if (p->insertcost < p->writecost
712 && p->insertcost < p->deletecost
713 && (write_follows_p || i < j))
1af1b1e8 714 {
89700111
GM
715 /* Insert is cheaper than deleting or writing lines. Leave
716 a hole in the result display that will be filled with
717 empty lines when the queue is emptied. */
718 queue->count = 0;
719 queue->window = i;
720 queue->pos = i - p->insertcount;
721 ++queue;
722
1af1b1e8 723 i -= p->insertcount;
89700111 724 write_follows_p = 0;
1af1b1e8 725 }
89700111
GM
726 else if (p->deletecost < p->writecost
727 && (write_follows_p || i > j))
1af1b1e8 728 {
89700111
GM
729 /* Deleting lines is cheaper. By decrementing J, omit
730 deletecount lines from the original. */
731 write_follows_p = 0;
1af1b1e8
RS
732 j -= p->deletecount;
733 }
734 else
735 {
89700111
GM
736 /* One or more lines should be written. In the direct
737 scrolling method we do this by scrolling the lines to the
738 place they belong. */
739 int n_to_write = p->writecount;
740 write_follows_p = 1;
741 xassert (n_to_write > 0);
742
1af1b1e8
RS
743 if (i > j)
744 {
89700111 745 /* Immediately insert lines */
1af1b1e8 746 set_terminal_window (i + unchanged_at_top);
89700111
GM
747 terminal_window_p = 1;
748 ins_del_lines (j - n_to_write + unchanged_at_top, i - j);
1af1b1e8
RS
749 }
750 else if (i < j)
751 {
89700111
GM
752 /* Queue the deletion of a group of lines */
753 queue->pos = i - n_to_write + unchanged_at_top;
754 queue->window = j + unchanged_at_top;
755 queue->count = i - j;
756 ++queue;
1af1b1e8
RS
757 }
758
89700111 759 while (n_to_write > 0)
1af1b1e8 760 {
89700111
GM
761 --i, --j, --n_to_write;
762 copy_from[i] = j;
763 retained_p[j] = 1;
1af1b1e8
RS
764 }
765 }
766 }
767
89700111
GM
768 /* Do queued operations. */
769 if (queue > queue_start)
1af1b1e8 770 {
89700111
GM
771 int next = -1;
772
773 do
1af1b1e8 774 {
89700111
GM
775 --queue;
776 if (queue->count)
777 {
778 set_terminal_window (queue->window);
779 terminal_window_p = 1;
780 ins_del_lines (queue->pos, queue->count);
781 }
782 else
1af1b1e8 783 {
89700111
GM
784 for (j = queue->window - 1; j >= queue->pos; --j)
785 {
786 while (retained_p[++next])
787 ;
788 copy_from[j] = next;
789 }
1af1b1e8
RS
790 }
791 }
89700111 792 while (queue > queue_start);
1af1b1e8
RS
793 }
794
89700111
GM
795 /* Now, for each row I in the range of rows we are working on,
796 copy_from[i] gives the original line to copy to I, and
797 retained_p[copy_from[i]] is zero if line I in the new display is
798 empty. */
799 mirrored_line_dance (current_matrix, unchanged_at_top, window_size,
800 copy_from, retained_p);
801
802 if (terminal_window_p)
1af1b1e8
RS
803 set_terminal_window (0);
804}
89700111
GM
805
806
1af1b1e8 807\f
632a8dd6 808void
0137dbf7 809scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
1af1b1e8 810 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
0137dbf7 811 FRAME_PTR frame;
632a8dd6
JB
812 int window_size, unchanged_at_top, unchanged_at_bottom;
813 int *draw_cost;
1af1b1e8 814 int *old_draw_cost;
632a8dd6
JB
815 int *old_hash;
816 int *new_hash;
817 int free_at_end;
818{
819 struct matrix_elt *matrix;
820 matrix = ((struct matrix_elt *)
821 alloca ((window_size + 1) * (window_size + 1) * sizeof *matrix));
822
1af1b1e8
RS
823 if (scroll_region_ok)
824 {
825 calculate_direct_scrolling (frame, matrix, window_size,
826 unchanged_at_bottom,
827 draw_cost, old_draw_cost,
828 old_hash, new_hash, free_at_end);
89700111
GM
829 do_direct_scrolling (frame->current_matrix,
830 matrix, window_size, unchanged_at_top);
1af1b1e8
RS
831 }
832 else
833 {
834 calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom,
835 draw_cost, old_hash, new_hash,
836 free_at_end);
89700111
GM
837 do_scrolling (frame->current_matrix, matrix, window_size,
838 unchanged_at_top);
1af1b1e8 839 }
632a8dd6 840}
89700111
GM
841
842
632a8dd6 843\f
89700111
GM
844/* Return number of lines in common between current and desired frame
845 contents described to us only as vectors of hash codes OLDHASH and
846 NEWHASH. Consider only vpos range START to END (not including
847 END). Ignore short lines on the assumption that avoiding redrawing
848 such a line will have little weight. */
632a8dd6
JB
849
850int
851scrolling_max_lines_saved (start, end, oldhash, newhash, cost)
852 int start, end;
853 int *oldhash, *newhash, *cost;
854{
855 struct { int hash; int count; } lines[01000];
856 register int i, h;
857 register int matchcount = 0;
858 int avg_length = 0;
859 int threshold;
860
861 /* Compute a threshold which is 1/4 of average length of these lines. */
862
863 for (i = start; i < end; i++)
864 avg_length += cost[i];
865
866 avg_length /= end - start;
867 threshold = avg_length / 4;
868
869 bzero (lines, sizeof lines);
870
89700111
GM
871 /* Put new lines' hash codes in hash table. Ignore lines shorter
872 than the threshold. Thus, if the lines that are in common are
873 mainly the ones that are short, they won't count. */
632a8dd6
JB
874 for (i = start; i < end; i++)
875 {
876 if (cost[i] > threshold)
877 {
878 h = newhash[i] & 0777;
879 lines[h].hash = newhash[i];
880 lines[h].count++;
881 }
882 }
883
89700111
GM
884 /* Look up old line hash codes in the hash table. Count number of
885 matches between old lines and new. */
632a8dd6
JB
886 for (i = start; i < end; i++)
887 {
888 h = oldhash[i] & 0777;
889 if (oldhash[i] == lines[h].hash)
890 {
891 matchcount++;
892 if (--lines[h].count == 0)
893 lines[h].hash = 0;
894 }
895 }
896
897 return matchcount;
898}
899\f
89700111
GM
900/* Return a measure of the cost of moving the lines starting with vpos
901 FROM, up to but not including vpos TO, down by AMOUNT lines (AMOUNT
902 may be negative). These are the same arguments that might be given
903 to scroll_frame_lines to perform this scrolling. */
632a8dd6 904
dfcf069d 905int
0137dbf7
JB
906scroll_cost (frame, from, to, amount)
907 FRAME_PTR frame;
632a8dd6
JB
908 int from, to, amount;
909{
0137dbf7 910 /* Compute how many lines, at bottom of frame,
632a8dd6
JB
911 will not be involved in actual motion. */
912 int limit = to;
913 int offset;
0137dbf7 914 int height = FRAME_HEIGHT (frame);
632a8dd6 915
632a8dd6
JB
916 if (amount == 0)
917 return 0;
918
3e2bea7c
JB
919 if (! scroll_region_ok)
920 limit = height;
921 else if (amount > 0)
922 limit += amount;
923
632a8dd6
JB
924 if (amount < 0)
925 {
926 int temp = to;
927 to = from + amount;
928 from = temp + amount;
929 amount = - amount;
930 }
931
932 offset = height - limit;
933
934 return
0137dbf7
JB
935 (FRAME_INSERT_COST (frame)[offset + from]
936 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
00eb4c4a
RS
937 + FRAME_DELETE_COST (frame)[offset + to]
938 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
632a8dd6
JB
939}
940\f
941/* Calculate the line insertion/deletion
942 overhead and multiply factor values */
943
944static void
0137dbf7
JB
945line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
946 FRAME_PTR frame;
632a8dd6
JB
947 int ov1, ovn;
948 int pf1, pfn;
949 register int *ov, *mf;
950{
951 register int i;
0137dbf7 952 register int frame_height = FRAME_HEIGHT (frame);
632a8dd6
JB
953 register int insert_overhead = ov1 * 10;
954 register int next_insert_cost = ovn * 10;
955
0137dbf7 956 for (i = frame_height-1; i >= 0; i--)
632a8dd6 957 {
326e0cf8 958 mf[i] = next_insert_cost / 10;
632a8dd6 959 next_insert_cost += pfn;
326e0cf8 960 ov[i] = (insert_overhead + next_insert_cost) / 10;
632a8dd6
JB
961 insert_overhead += pf1;
962 }
963}
964
965static void
0137dbf7 966ins_del_costs (frame,
632a8dd6
JB
967 one_line_string, multi_string,
968 setup_string, cleanup_string,
969 costvec, ncostvec, coefficient)
0137dbf7 970 FRAME_PTR frame;
632a8dd6
JB
971 char *one_line_string, *multi_string;
972 char *setup_string, *cleanup_string;
973 int *costvec, *ncostvec;
974 int coefficient;
975{
976 if (multi_string)
0137dbf7 977 line_ins_del (frame,
632a8dd6
JB
978 string_cost (multi_string) * coefficient,
979 per_line_cost (multi_string) * coefficient,
980 0, 0, costvec, ncostvec);
981 else if (one_line_string)
0137dbf7 982 line_ins_del (frame,
632a8dd6
JB
983 string_cost (setup_string) + string_cost (cleanup_string), 0,
984 string_cost (one_line_string),
985 per_line_cost (one_line_string),
986 costvec, ncostvec);
987 else
0137dbf7 988 line_ins_del (frame,
632a8dd6
JB
989 9999, 0, 9999, 0,
990 costvec, ncostvec);
991}
992
993/* Calculate the insert and delete line costs.
994 Note that this is done even when running with a window system
995 because we want to know how long scrolling takes (and avoid it).
0137dbf7 996 This must be redone whenever the frame height changes.
632a8dd6
JB
997
998 We keep the ID costs in a precomputed array based on the position
999 at which the I or D is performed. Also, there are two kinds of ID
1000 costs: the "once-only" and the "repeated". This is to handle both
1001 those terminals that are able to insert N lines at a time (once-
1002 only) and those that must repeatedly insert one line.
1003
1004 The cost to insert N lines at line L is
0137dbf7
JB
1005 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
1006 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
632a8dd6
JB
1007
1008 ILov represents the basic insert line overhead. ILpf is the padding
1009 required to allow the terminal time to move a line: insertion at line
0137dbf7 1010 L changes (frame_height + 1 - L) lines.
632a8dd6
JB
1011
1012 The first bracketed expression above is the overhead; the second is
1013 the multiply factor. Both are dependent only on the position at
1014 which the insert is performed. We store the overhead in
0137dbf7
JB
1015 FRAME_INSERT_COST (frame) and the multiply factor in
1016 FRAME_INSERTN_COST (frame). Note however that any insertion
632a8dd6 1017 must include at least one multiply factor. Rather than compute this
0137dbf7
JB
1018 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1019 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
632a8dd6
JB
1020 This is reasonable because of the particular algorithm used in calcM.
1021
1022 Deletion is essentially the same as insertion.
1023 */
1024
dfcf069d 1025void
0137dbf7 1026do_line_insertion_deletion_costs (frame,
632a8dd6
JB
1027 ins_line_string, multi_ins_string,
1028 del_line_string, multi_del_string,
1029 setup_string, cleanup_string, coefficient)
0137dbf7 1030 FRAME_PTR frame;
632a8dd6
JB
1031 char *ins_line_string, *multi_ins_string;
1032 char *del_line_string, *multi_del_string;
1033 char *setup_string, *cleanup_string;
1034 int coefficient;
1035{
0137dbf7 1036 if (FRAME_INSERT_COST (frame) != 0)
632a8dd6 1037 {
0137dbf7
JB
1038 FRAME_INSERT_COST (frame) =
1039 (int *) xrealloc (FRAME_INSERT_COST (frame),
1040 FRAME_HEIGHT (frame) * sizeof (int));
1041 FRAME_DELETEN_COST (frame) =
1042 (int *) xrealloc (FRAME_DELETEN_COST (frame),
1043 FRAME_HEIGHT (frame) * sizeof (int));
1044 FRAME_INSERTN_COST (frame) =
1045 (int *) xrealloc (FRAME_INSERTN_COST (frame),
1046 FRAME_HEIGHT (frame) * sizeof (int));
1047 FRAME_DELETE_COST (frame) =
1048 (int *) xrealloc (FRAME_DELETE_COST (frame),
1049 FRAME_HEIGHT (frame) * sizeof (int));
632a8dd6
JB
1050 }
1051 else
1052 {
0137dbf7
JB
1053 FRAME_INSERT_COST (frame) =
1054 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1055 FRAME_DELETEN_COST (frame) =
1056 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1057 FRAME_INSERTN_COST (frame) =
1058 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1059 FRAME_DELETE_COST (frame) =
1060 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
632a8dd6
JB
1061 }
1062
0137dbf7 1063 ins_del_costs (frame,
632a8dd6
JB
1064 ins_line_string, multi_ins_string,
1065 setup_string, cleanup_string,
0137dbf7 1066 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
632a8dd6 1067 coefficient);
0137dbf7 1068 ins_del_costs (frame,
632a8dd6
JB
1069 del_line_string, multi_del_string,
1070 setup_string, cleanup_string,
fd374ddc 1071 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
632a8dd6
JB
1072 coefficient);
1073}