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