Fix -Wimplicit warnings.
[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 266 bcopy (current_frame->charstarts, temp_frame->charstarts,
dfcf069d 267 current_frame->height * sizeof (int *));
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
dfcf069d 892int
0137dbf7
JB
893scroll_cost (frame, from, to, amount)
894 FRAME_PTR frame;
632a8dd6
JB
895 int from, to, amount;
896{
0137dbf7 897 /* Compute how many lines, at bottom of frame,
632a8dd6
JB
898 will not be involved in actual motion. */
899 int limit = to;
900 int offset;
0137dbf7 901 int height = FRAME_HEIGHT (frame);
632a8dd6 902
632a8dd6
JB
903 if (amount == 0)
904 return 0;
905
3e2bea7c
JB
906 if (! scroll_region_ok)
907 limit = height;
908 else if (amount > 0)
909 limit += amount;
910
632a8dd6
JB
911 if (amount < 0)
912 {
913 int temp = to;
914 to = from + amount;
915 from = temp + amount;
916 amount = - amount;
917 }
918
919 offset = height - limit;
920
921 return
0137dbf7
JB
922 (FRAME_INSERT_COST (frame)[offset + from]
923 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
00eb4c4a
RS
924 + FRAME_DELETE_COST (frame)[offset + to]
925 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
632a8dd6
JB
926}
927\f
928/* Calculate the line insertion/deletion
929 overhead and multiply factor values */
930
931static void
0137dbf7
JB
932line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
933 FRAME_PTR frame;
632a8dd6
JB
934 int ov1, ovn;
935 int pf1, pfn;
936 register int *ov, *mf;
937{
938 register int i;
0137dbf7 939 register int frame_height = FRAME_HEIGHT (frame);
632a8dd6
JB
940 register int insert_overhead = ov1 * 10;
941 register int next_insert_cost = ovn * 10;
942
0137dbf7 943 for (i = frame_height-1; i >= 0; i--)
632a8dd6 944 {
326e0cf8 945 mf[i] = next_insert_cost / 10;
632a8dd6 946 next_insert_cost += pfn;
326e0cf8 947 ov[i] = (insert_overhead + next_insert_cost) / 10;
632a8dd6
JB
948 insert_overhead += pf1;
949 }
950}
951
952static void
0137dbf7 953ins_del_costs (frame,
632a8dd6
JB
954 one_line_string, multi_string,
955 setup_string, cleanup_string,
956 costvec, ncostvec, coefficient)
0137dbf7 957 FRAME_PTR frame;
632a8dd6
JB
958 char *one_line_string, *multi_string;
959 char *setup_string, *cleanup_string;
960 int *costvec, *ncostvec;
961 int coefficient;
962{
963 if (multi_string)
0137dbf7 964 line_ins_del (frame,
632a8dd6
JB
965 string_cost (multi_string) * coefficient,
966 per_line_cost (multi_string) * coefficient,
967 0, 0, costvec, ncostvec);
968 else if (one_line_string)
0137dbf7 969 line_ins_del (frame,
632a8dd6
JB
970 string_cost (setup_string) + string_cost (cleanup_string), 0,
971 string_cost (one_line_string),
972 per_line_cost (one_line_string),
973 costvec, ncostvec);
974 else
0137dbf7 975 line_ins_del (frame,
632a8dd6
JB
976 9999, 0, 9999, 0,
977 costvec, ncostvec);
978}
979
980/* Calculate the insert and delete line costs.
981 Note that this is done even when running with a window system
982 because we want to know how long scrolling takes (and avoid it).
0137dbf7 983 This must be redone whenever the frame height changes.
632a8dd6
JB
984
985 We keep the ID costs in a precomputed array based on the position
986 at which the I or D is performed. Also, there are two kinds of ID
987 costs: the "once-only" and the "repeated". This is to handle both
988 those terminals that are able to insert N lines at a time (once-
989 only) and those that must repeatedly insert one line.
990
991 The cost to insert N lines at line L is
0137dbf7
JB
992 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
993 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
632a8dd6
JB
994
995 ILov represents the basic insert line overhead. ILpf is the padding
996 required to allow the terminal time to move a line: insertion at line
0137dbf7 997 L changes (frame_height + 1 - L) lines.
632a8dd6
JB
998
999 The first bracketed expression above is the overhead; the second is
1000 the multiply factor. Both are dependent only on the position at
1001 which the insert is performed. We store the overhead in
0137dbf7
JB
1002 FRAME_INSERT_COST (frame) and the multiply factor in
1003 FRAME_INSERTN_COST (frame). Note however that any insertion
632a8dd6 1004 must include at least one multiply factor. Rather than compute this
0137dbf7
JB
1005 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1006 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
632a8dd6
JB
1007 This is reasonable because of the particular algorithm used in calcM.
1008
1009 Deletion is essentially the same as insertion.
1010 */
1011
dfcf069d 1012void
0137dbf7 1013do_line_insertion_deletion_costs (frame,
632a8dd6
JB
1014 ins_line_string, multi_ins_string,
1015 del_line_string, multi_del_string,
1016 setup_string, cleanup_string, coefficient)
0137dbf7 1017 FRAME_PTR frame;
632a8dd6
JB
1018 char *ins_line_string, *multi_ins_string;
1019 char *del_line_string, *multi_del_string;
1020 char *setup_string, *cleanup_string;
1021 int coefficient;
1022{
0137dbf7 1023 if (FRAME_INSERT_COST (frame) != 0)
632a8dd6 1024 {
0137dbf7
JB
1025 FRAME_INSERT_COST (frame) =
1026 (int *) xrealloc (FRAME_INSERT_COST (frame),
1027 FRAME_HEIGHT (frame) * sizeof (int));
1028 FRAME_DELETEN_COST (frame) =
1029 (int *) xrealloc (FRAME_DELETEN_COST (frame),
1030 FRAME_HEIGHT (frame) * sizeof (int));
1031 FRAME_INSERTN_COST (frame) =
1032 (int *) xrealloc (FRAME_INSERTN_COST (frame),
1033 FRAME_HEIGHT (frame) * sizeof (int));
1034 FRAME_DELETE_COST (frame) =
1035 (int *) xrealloc (FRAME_DELETE_COST (frame),
1036 FRAME_HEIGHT (frame) * sizeof (int));
632a8dd6
JB
1037 }
1038 else
1039 {
0137dbf7
JB
1040 FRAME_INSERT_COST (frame) =
1041 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1042 FRAME_DELETEN_COST (frame) =
1043 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1044 FRAME_INSERTN_COST (frame) =
1045 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
1046 FRAME_DELETE_COST (frame) =
1047 (int *) xmalloc (FRAME_HEIGHT (frame) * sizeof (int));
632a8dd6
JB
1048 }
1049
0137dbf7 1050 ins_del_costs (frame,
632a8dd6
JB
1051 ins_line_string, multi_ins_string,
1052 setup_string, cleanup_string,
0137dbf7 1053 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
632a8dd6 1054 coefficient);
0137dbf7 1055 ins_del_costs (frame,
632a8dd6
JB
1056 del_line_string, multi_del_string,
1057 setup_string, cleanup_string,
fd374ddc 1058 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
632a8dd6
JB
1059 coefficient);
1060}