(scan_lists): Follow coding convention.
[bpt/emacs.git] / src / scroll.c
CommitLineData
632a8dd6 1/* Calculate what line insertion or deletion to do, and do it,
429ab54e 2 Copyright (C) 1985, 1986, 1990, 1993, 1994, 2001, 2002, 2003, 2004,
8cabe764 3 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
632a8dd6
JB
4
5This file is part of GNU Emacs.
6
7GNU Emacs is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
684d6f5b 9the Free Software Foundation; either version 3, or (at your option)
632a8dd6
JB
10any later version.
11
12GNU Emacs is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Emacs; see the file COPYING. If not, write to
4fc5845f
LK
19the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20Boston, MA 02110-1301, USA. */
632a8dd6
JB
21
22
18160b98 23#include <config.h>
89700111
GM
24#include <stdio.h>
25#include <string.h>
9628b887 26#include "lisp.h"
632a8dd6 27#include "termchar.h"
632a8dd6 28#include "dispextern.h"
2538fae4 29#include "keyboard.h"
0137dbf7 30#include "frame.h"
89700111 31#include "window.h"
daf01701 32#include "termhooks.h"
632a8dd6 33
632a8dd6 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
385ed61f
KL
62static void do_direct_scrolling P_ ((struct frame *,
63 struct glyph_matrix *,
89700111
GM
64 struct matrix_elt *,
65 int, int));
385ed61f
KL
66static void do_scrolling P_ ((struct frame *,
67 struct glyph_matrix *,
89700111
GM
68 struct matrix_elt *,
69 int, int));
70
632a8dd6 71\f
1af1b1e8
RS
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.
632a8dd6
JB
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.
0137dbf7 85 Note that these are not true frame vpos's, but relative
632a8dd6
JB
86 to the place at which the first mismatch between old and
87 new contents appears. */
88
89static void
0137dbf7 90calculate_scrolling (frame, matrix, window_size, lines_below,
632a8dd6
JB
91 draw_cost, old_hash, new_hash,
92 free_at_end)
0137dbf7 93 FRAME_PTR frame;
632a8dd6
JB
94 /* matrix is of size window_size + 1 on each side. */
95 struct matrix_elt *matrix;
250368af 96 int window_size, lines_below;
632a8dd6
JB
97 int *draw_cost;
98 int *old_hash;
99 int *new_hash;
100 int free_at_end;
101{
102 register int i, j;
9882535b 103 int frame_lines = FRAME_LINES (frame);
632a8dd6
JB
104 register struct matrix_elt *p, *p1;
105 register int cost, cost1;
106
8a56675d 107 int lines_moved = window_size
daf01701 108 + (FRAME_SCROLL_REGION_OK (frame) ? 0 : lines_below);
632a8dd6 109 /* first_insert_cost[I] is the cost of doing the first insert-line
89700111 110 at the i'th line of the lines we are considering,
632a8dd6
JB
111 where I is origin 1 (as it is below). */
112 int *first_insert_cost
9882535b 113 = &FRAME_INSERT_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 114 int *first_delete_cost
9882535b 115 = &FRAME_DELETE_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 116 int *next_insert_cost
9882535b 117 = &FRAME_INSERTN_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6 118 int *next_delete_cost
9882535b 119 = &FRAME_DELETEN_COST (frame)[frame_lines - 1 - lines_moved];
632a8dd6
JB
120
121 /* Discourage long scrolls on fast lines.
0137dbf7 122 Don't scroll nearly a full frame height unless it saves
632a8dd6 123 at least 1/4 second. */
9882535b 124 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
632a8dd6 125
7fb08f69
RS
126 if (baud_rate <= 0)
127 extra_cost = 1;
128
632a8dd6
JB
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
0137dbf7
JB
161 /* `i' represents the vpos among new frame contents.
162 `j' represents the vpos among the old frame contents. */
632a8dd6
JB
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];
0fe4741d 203 if ((int) p1->insertcount > i)
632a8dd6
JB
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;
0fe4741d 209 if ((int) p->insertcount > i)
632a8dd6
JB
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}
89700111
GM
234
235
632a8dd6 236\f
89700111
GM
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
177c0ea7 240 scroll windows (scroll_region_ok == 0).
2d810f84
RS
241
242 WINDOW_SIZE is the number of lines being considered for scrolling
89700111
GM
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. */
177c0ea7 246
632a8dd6 247static void
385ed61f
KL
248do_scrolling (frame, current_matrix, matrix, window_size, unchanged_at_top)
249 struct frame *frame;
89700111 250 struct glyph_matrix *current_matrix;
632a8dd6
JB
251 struct matrix_elt *matrix;
252 int window_size;
253 int unchanged_at_top;
254{
89700111
GM
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;
177c0ea7 267
89700111
GM
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
b96f9fb7 276#define CHECK_BOUNDS \
89700111
GM
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. */
632a8dd6 288 i = j = window_size;
632a8dd6
JB
289 while (i > 0 || j > 0)
290 {
291 p = matrix + i * (window_size + 1) + j;
177c0ea7 292
89700111 293 if (p->insertcost < p->writecost && p->insertcost < p->deletecost)
632a8dd6 294 {
89700111
GM
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. */
632a8dd6 303 i -= p->insertcount;
632a8dd6
JB
304 }
305 else if (p->deletecost < p->writecost)
306 {
89700111
GM
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. */
632a8dd6 311 j -= p->deletecount;
89700111
GM
312
313 /* Set the terminal window, if not done already. */
314 if (! terminal_window_p)
632a8dd6 315 {
385ed61f 316 set_terminal_window (frame, window_size + unchanged_at_top);
89700111 317 terminal_window_p = 1;
632a8dd6 318 }
89700111
GM
319
320 /* Delete lines on the terminal. */
385ed61f 321 ins_del_lines (frame, j + unchanged_at_top, - p->deletecount);
632a8dd6
JB
322 }
323 else
324 {
89700111
GM
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
b96f9fb7 333 CHECK_BOUNDS;
89700111 334#endif
632a8dd6
JB
335 }
336 }
337
89700111
GM
338 /* Now do all insertions queued above. */
339 if (queue > queue_start)
632a8dd6 340 {
89700111 341 int next = -1;
632a8dd6 342
89700111
GM
343 /* Set the terminal window if not yet done. */
344 if (!terminal_window_p)
345 {
385ed61f 346 set_terminal_window (frame, window_size + unchanged_at_top);
89700111
GM
347 terminal_window_p = 1;
348 }
632a8dd6 349
89700111 350 do
632a8dd6 351 {
89700111
GM
352 --queue;
353
354 /* Do the deletion on the terminal. */
385ed61f 355 ins_del_lines (frame, queue->pos, queue->count);
89700111
GM
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 }
632a8dd6 372 }
89700111 373 while (queue > queue_start);
177c0ea7 374
632a8dd6
JB
375 }
376
89700111
GM
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);
177c0ea7 386
89700111 387 if (terminal_window_p)
385ed61f 388 set_terminal_window (frame, 0);
632a8dd6 389}
89700111 390
632a8dd6 391\f
1af1b1e8
RS
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
432static void
433calculate_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;
250368af 439 int window_size, lines_below;
1af1b1e8
RS
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;
9882535b 447 int frame_lines = FRAME_LINES (frame);
1af1b1e8
RS
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
9882535b 454 = &FRAME_INSERT_COST (frame)[frame_lines - 1];
1af1b1e8 455 int *first_delete_cost
9882535b 456 = &FRAME_DELETE_COST (frame)[frame_lines - 1];
1af1b1e8 457 int *next_insert_cost
9882535b 458 = &FRAME_INSERTN_COST (frame)[frame_lines - 1];
1af1b1e8 459 int *next_delete_cost
9882535b 460 = &FRAME_DELETEN_COST (frame)[frame_lines - 1];
1af1b1e8
RS
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. */
9882535b 467 int extra_cost = baud_rate / (10 * 4 * FRAME_LINES (frame));
1af1b1e8
RS
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 */
8a56675d 475 scroll_overhead
daf01701 476 = FRAME_SCROLL_REGION_COST (frame) + extra_cost;
1af1b1e8
RS
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] +
177c0ea7 572 (delta-1) * (next_insert_cost[-j] - next_insert_cost[1-j]);
1af1b1e8
RS
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] +
177c0ea7 580 (delta-1) * ( next_delete_cost[-i] - next_delete_cost[1-i]);
1af1b1e8
RS
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}
89700111
GM
639
640
1af1b1e8 641\f
89700111
GM
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).
1af1b1e8
RS
646
647 WINDOW_SIZE is the number of lines being considered for scrolling
89700111
GM
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.
177c0ea7 651
1af1b1e8
RS
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
89700111
GM
655 is described in more detail in calculate_direct_scrolling, where
656 the cost matrix for this approach is constructed. */
1af1b1e8
RS
657
658static void
385ed61f
KL
659do_direct_scrolling (frame, current_matrix, cost_matrix,
660 window_size, unchanged_at_top)
661 struct frame *frame;
89700111
GM
662 struct glyph_matrix *current_matrix;
663 struct matrix_elt *cost_matrix;
1af1b1e8
RS
664 int window_size;
665 int unchanged_at_top;
666{
89700111
GM
667 struct matrix_elt *p;
668 int i, j;
1af1b1e8 669
89700111
GM
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;
1af1b1e8 675
89700111
GM
676 /* Set to 1 if a terminal window has been set with
677 set_terminal_window: */
678 int terminal_window_p = 0;
1af1b1e8 679
89700111
GM
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;
177c0ea7 712
1af1b1e8
RS
713 while (i > 0 || j > 0)
714 {
89700111 715 p = cost_matrix + i * (window_size + 1) + j;
177c0ea7 716
89700111
GM
717 if (p->insertcost < p->writecost
718 && p->insertcost < p->deletecost
719 && (write_follows_p || i < j))
1af1b1e8 720 {
89700111
GM
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;
177c0ea7 728
1af1b1e8 729 i -= p->insertcount;
89700111 730 write_follows_p = 0;
1af1b1e8 731 }
89700111
GM
732 else if (p->deletecost < p->writecost
733 && (write_follows_p || i > j))
1af1b1e8 734 {
89700111
GM
735 /* Deleting lines is cheaper. By decrementing J, omit
736 deletecount lines from the original. */
737 write_follows_p = 0;
1af1b1e8
RS
738 j -= p->deletecount;
739 }
740 else
741 {
89700111
GM
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;
177c0ea7 747 xassert (n_to_write > 0);
89700111 748
1af1b1e8
RS
749 if (i > j)
750 {
89700111 751 /* Immediately insert lines */
385ed61f 752 set_terminal_window (frame, i + unchanged_at_top);
89700111 753 terminal_window_p = 1;
385ed61f 754 ins_del_lines (frame, j - n_to_write + unchanged_at_top, i - j);
1af1b1e8
RS
755 }
756 else if (i < j)
757 {
89700111
GM
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;
1af1b1e8
RS
763 }
764
89700111 765 while (n_to_write > 0)
1af1b1e8 766 {
89700111
GM
767 --i, --j, --n_to_write;
768 copy_from[i] = j;
769 retained_p[j] = 1;
1af1b1e8
RS
770 }
771 }
772 }
773
89700111
GM
774 /* Do queued operations. */
775 if (queue > queue_start)
1af1b1e8 776 {
89700111
GM
777 int next = -1;
778
779 do
1af1b1e8 780 {
89700111
GM
781 --queue;
782 if (queue->count)
783 {
385ed61f 784 set_terminal_window (frame, queue->window);
89700111 785 terminal_window_p = 1;
385ed61f 786 ins_del_lines (frame, queue->pos, queue->count);
89700111
GM
787 }
788 else
1af1b1e8 789 {
89700111
GM
790 for (j = queue->window - 1; j >= queue->pos; --j)
791 {
792 while (retained_p[++next])
793 ;
794 copy_from[j] = next;
795 }
1af1b1e8
RS
796 }
797 }
89700111 798 while (queue > queue_start);
1af1b1e8
RS
799 }
800
89700111
GM
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)
385ed61f 809 set_terminal_window (frame, 0);
1af1b1e8 810}
89700111
GM
811
812
1af1b1e8 813\f
632a8dd6 814void
0137dbf7 815scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
1af1b1e8 816 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
0137dbf7 817 FRAME_PTR frame;
632a8dd6
JB
818 int window_size, unchanged_at_top, unchanged_at_bottom;
819 int *draw_cost;
1af1b1e8 820 int *old_draw_cost;
632a8dd6
JB
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
daf01701 829 if (FRAME_SCROLL_REGION_OK (frame))
1af1b1e8
RS
830 {
831 calculate_direct_scrolling (frame, matrix, window_size,
832 unchanged_at_bottom,
177c0ea7 833 draw_cost, old_draw_cost,
1af1b1e8 834 old_hash, new_hash, free_at_end);
385ed61f 835 do_direct_scrolling (frame, frame->current_matrix,
89700111 836 matrix, window_size, unchanged_at_top);
1af1b1e8
RS
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);
385ed61f
KL
843 do_scrolling (frame,
844 frame->current_matrix, matrix, window_size,
89700111 845 unchanged_at_top);
1af1b1e8 846 }
632a8dd6 847}
89700111
GM
848
849
632a8dd6 850\f
89700111
GM
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. */
632a8dd6
JB
856
857int
858scrolling_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
89700111
GM
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. */
632a8dd6
JB
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
89700111
GM
891 /* Look up old line hash codes in the hash table. Count number of
892 matches between old lines and new. */
632a8dd6
JB
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
89700111
GM
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. */
632a8dd6 911
dfcf069d 912int
0137dbf7
JB
913scroll_cost (frame, from, to, amount)
914 FRAME_PTR frame;
632a8dd6
JB
915 int from, to, amount;
916{
0137dbf7 917 /* Compute how many lines, at bottom of frame,
632a8dd6
JB
918 will not be involved in actual motion. */
919 int limit = to;
920 int offset;
9882535b 921 int height = FRAME_LINES (frame);
632a8dd6 922
632a8dd6
JB
923 if (amount == 0)
924 return 0;
925
daf01701 926 if (! FRAME_SCROLL_REGION_OK (frame))
3e2bea7c
JB
927 limit = height;
928 else if (amount > 0)
929 limit += amount;
930
632a8dd6
JB
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
0137dbf7
JB
942 (FRAME_INSERT_COST (frame)[offset + from]
943 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
00eb4c4a
RS
944 + FRAME_DELETE_COST (frame)[offset + to]
945 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
632a8dd6
JB
946}
947\f
948/* Calculate the line insertion/deletion
949 overhead and multiply factor values */
950
951static void
0137dbf7
JB
952line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
953 FRAME_PTR frame;
632a8dd6
JB
954 int ov1, ovn;
955 int pf1, pfn;
956 register int *ov, *mf;
957{
958 register int i;
9882535b 959 register int frame_lines = FRAME_LINES (frame);
632a8dd6
JB
960 register int insert_overhead = ov1 * 10;
961 register int next_insert_cost = ovn * 10;
962
9882535b 963 for (i = frame_lines-1; i >= 0; i--)
632a8dd6 964 {
326e0cf8 965 mf[i] = next_insert_cost / 10;
632a8dd6 966 next_insert_cost += pfn;
326e0cf8 967 ov[i] = (insert_overhead + next_insert_cost) / 10;
632a8dd6
JB
968 insert_overhead += pf1;
969 }
970}
971
972static void
0137dbf7 973ins_del_costs (frame,
632a8dd6
JB
974 one_line_string, multi_string,
975 setup_string, cleanup_string,
976 costvec, ncostvec, coefficient)
0137dbf7 977 FRAME_PTR frame;
632a8dd6
JB
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)
0137dbf7 984 line_ins_del (frame,
632a8dd6
JB
985 string_cost (multi_string) * coefficient,
986 per_line_cost (multi_string) * coefficient,
987 0, 0, costvec, ncostvec);
988 else if (one_line_string)
0137dbf7 989 line_ins_del (frame,
632a8dd6
JB
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
0137dbf7 995 line_ins_del (frame,
632a8dd6
JB
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).
0137dbf7 1003 This must be redone whenever the frame height changes.
632a8dd6
JB
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
9882535b
KS
1012 [tt.t_ILov + (frame_lines + 1 - L) * tt.t_ILpf] +
1013 N * [tt.t_ILnov + (frame_lines + 1 - L) * tt.t_ILnpf]
632a8dd6
JB
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
9882535b 1017 L changes (frame_lines + 1 - L) lines.
632a8dd6
JB
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
0137dbf7
JB
1022 FRAME_INSERT_COST (frame) and the multiply factor in
1023 FRAME_INSERTN_COST (frame). Note however that any insertion
632a8dd6 1024 must include at least one multiply factor. Rather than compute this
0137dbf7
JB
1025 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1026 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
632a8dd6
JB
1027 This is reasonable because of the particular algorithm used in calcM.
1028
1029 Deletion is essentially the same as insertion.
1030 */
1031
dfcf069d 1032void
0137dbf7 1033do_line_insertion_deletion_costs (frame,
632a8dd6
JB
1034 ins_line_string, multi_ins_string,
1035 del_line_string, multi_del_string,
1036 setup_string, cleanup_string, coefficient)
0137dbf7 1037 FRAME_PTR frame;
632a8dd6
JB
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{
0137dbf7 1043 if (FRAME_INSERT_COST (frame) != 0)
632a8dd6 1044 {
0137dbf7
JB
1045 FRAME_INSERT_COST (frame) =
1046 (int *) xrealloc (FRAME_INSERT_COST (frame),
9882535b 1047 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1048 FRAME_DELETEN_COST (frame) =
1049 (int *) xrealloc (FRAME_DELETEN_COST (frame),
9882535b 1050 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1051 FRAME_INSERTN_COST (frame) =
1052 (int *) xrealloc (FRAME_INSERTN_COST (frame),
9882535b 1053 FRAME_LINES (frame) * sizeof (int));
0137dbf7
JB
1054 FRAME_DELETE_COST (frame) =
1055 (int *) xrealloc (FRAME_DELETE_COST (frame),
9882535b 1056 FRAME_LINES (frame) * sizeof (int));
632a8dd6
JB
1057 }
1058 else
1059 {
0137dbf7 1060 FRAME_INSERT_COST (frame) =
9882535b 1061 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
0137dbf7 1062 FRAME_DELETEN_COST (frame) =
9882535b 1063 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
0137dbf7 1064 FRAME_INSERTN_COST (frame) =
9882535b 1065 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
177c0ea7 1066 FRAME_DELETE_COST (frame) =
9882535b 1067 (int *) xmalloc (FRAME_LINES (frame) * sizeof (int));
632a8dd6
JB
1068 }
1069
0137dbf7 1070 ins_del_costs (frame,
632a8dd6
JB
1071 ins_line_string, multi_ins_string,
1072 setup_string, cleanup_string,
0137dbf7 1073 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
632a8dd6 1074 coefficient);
0137dbf7 1075 ins_del_costs (frame,
632a8dd6
JB
1076 del_line_string, multi_del_string,
1077 setup_string, cleanup_string,
fd374ddc 1078 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
632a8dd6
JB
1079 coefficient);
1080}
ab5796a9
MB
1081
1082/* arch-tag: cdb7149c-48e7-4793-a948-2786c8e45485
1083 (do not change this comment) */