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