Delete all menu-enable properties.
[bpt/emacs.git] / src / scroll.c
1 /* Calculate what line insertion or deletion to do, and do it,
2 Copyright (C) 1985, 1986, 1990, 1993, 1994 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21
22 #include <config.h>
23 #include "termchar.h"
24 #include "lisp.h"
25 #include "dispextern.h"
26 #include "frame.h"
27
28 extern 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.
34 So no cost can exceed the area of a frame, measured in characters.
35 Let's hope this is never more than 1000000 characters. */
36
37 #define INFINITY 1000000
38
39 struct matrix_elt
40 {
41 /* Cost of outputting through this line
42 if no insert/delete is done just above it. */
43 int writecost;
44 /* Cost of outputting through this line
45 if an insert is done just above it. */
46 int insertcost;
47 /* Cost of outputting through this line
48 if a delete is done just above it. */
49 int deletecost;
50 /* Number of inserts so far in this run of inserts,
51 for the cost in insertcost. */
52 unsigned char insertcount;
53 /* Number of deletes so far in this run of deletes,
54 for the cost in deletecost. */
55 unsigned char deletecount;
56 /* Number of writes so far since the last insert
57 or delete for the cost in writecost. */
58 unsigned char writecount;
59 };
60
61 \f
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.
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.
75 Note that these are not true frame vpos's, but relative
76 to the place at which the first mismatch between old and
77 new contents appears. */
78
79 static void
80 calculate_scrolling (frame, matrix, window_size, lines_below,
81 draw_cost, old_hash, new_hash,
82 free_at_end)
83 FRAME_PTR frame;
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;
93 int frame_height = FRAME_HEIGHT (frame);
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
102 = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved];
103 int *first_delete_cost
104 = &FRAME_DELETE_COST (frame)[frame_height - 1 - lines_moved];
105 int *next_insert_cost
106 = &FRAME_INSERTN_COST (frame)[frame_height - 1 - lines_moved];
107 int *next_delete_cost
108 = &FRAME_DELETEN_COST (frame)[frame_height - 1 - lines_moved];
109
110 /* Discourage long scrolls on fast lines.
111 Don't scroll nearly a full frame height unless it saves
112 at least 1/4 second. */
113 int extra_cost = baud_rate / (10 * 4 * FRAME_HEIGHT (frame));
114
115 if (baud_rate <= 0)
116 extra_cost = 1;
117
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
150 /* `i' represents the vpos among new frame contents.
151 `j' represents the vpos among the old frame contents. */
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];
192 if ((int) p1->insertcount > i)
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;
198 if ((int) p->insertcount > i)
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
224 /* Perform insert-lines and delete-lines operations on FRAME according
225 to the costs in MATRIX, using the general scrolling method.
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. */
238
239 static void
240 do_scrolling (frame, matrix, window_size, unchanged_at_top)
241 FRAME_PTR frame;
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;
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;
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
258 queue = (struct queue *) alloca (FRAME_HEIGHT (frame)
259 * sizeof (struct queue));
260
261 current_frame = FRAME_CURRENT_GLYPHS (frame);
262 temp_frame = FRAME_TEMP_GLYPHS (frame);
263
264 bcopy (current_frame->glyphs, temp_frame->glyphs,
265 current_frame->height * sizeof (GLYPH *));
266 bcopy (current_frame->charstarts, temp_frame->charstarts,
267 current_frame->height * sizeof (GLYPH *));
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));
275
276 #ifdef HAVE_WINDOW_SYSTEM
277 if (FRAME_WINDOW_P (frame))
278 {
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));
287 bcopy (current_frame->max_ascent, temp_frame->max_ascent,
288 current_frame->height * sizeof (short));
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 */
321 current_frame->glyphs[i + offset - 1]
322 = temp_frame->glyphs[j + offset - 1];
323 current_frame->charstarts[i + offset - 1]
324 = temp_frame->charstarts[j + offset - 1];
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;
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.
352 Those are the ones for which temp_frame->enable was not set. */
353 tem = queue[i].pos;
354 for (j = tem + queue[i].count - 1; j >= tem; j--)
355 {
356 current_frame->enable[j] = 0;
357 while (temp_frame->enable[next])
358 next++;
359 current_frame->glyphs[j] = temp_frame->glyphs[next];
360 current_frame->charstarts[j] = temp_frame->charstarts[next++];
361 }
362 }
363
364 if (window)
365 set_terminal_window (0);
366 }
367 \f
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
408 static void
409 calculate_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
636 static void
637 do_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
682 #ifdef HAVE_WINDOW_SYSTEM
683 if (FRAME_WINDOW_P (frame))
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
795 void
796 scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom,
797 draw_cost, old_draw_cost, old_hash, new_hash, free_at_end)
798 FRAME_PTR frame;
799 int window_size, unchanged_at_top, unchanged_at_bottom;
800 int *draw_cost;
801 int *old_draw_cost;
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
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 }
825 }
826 \f
827 /* Return number of lines in common between current and desired frame contents
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
833 int
834 scrolling_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
890 scroll_frame_lines to perform this scrolling. */
891
892 scroll_cost (frame, from, to, amount)
893 FRAME_PTR frame;
894 int from, to, amount;
895 {
896 /* Compute how many lines, at bottom of frame,
897 will not be involved in actual motion. */
898 int limit = to;
899 int offset;
900 int height = FRAME_HEIGHT (frame);
901
902 if (amount == 0)
903 return 0;
904
905 if (! scroll_region_ok)
906 limit = height;
907 else if (amount > 0)
908 limit += amount;
909
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
921 (FRAME_INSERT_COST (frame)[offset + from]
922 + (amount - 1) * FRAME_INSERTN_COST (frame)[offset + from]
923 + FRAME_DELETE_COST (frame)[offset + to]
924 + (amount - 1) * FRAME_DELETEN_COST (frame)[offset + to]);
925 }
926 \f
927 /* Calculate the line insertion/deletion
928 overhead and multiply factor values */
929
930 static void
931 line_ins_del (frame, ov1, pf1, ovn, pfn, ov, mf)
932 FRAME_PTR frame;
933 int ov1, ovn;
934 int pf1, pfn;
935 register int *ov, *mf;
936 {
937 register int i;
938 register int frame_height = FRAME_HEIGHT (frame);
939 register int insert_overhead = ov1 * 10;
940 register int next_insert_cost = ovn * 10;
941
942 for (i = frame_height-1; i >= 0; i--)
943 {
944 mf[i] = next_insert_cost / 10;
945 next_insert_cost += pfn;
946 ov[i] = (insert_overhead + next_insert_cost) / 10;
947 insert_overhead += pf1;
948 }
949 }
950
951 static void
952 ins_del_costs (frame,
953 one_line_string, multi_string,
954 setup_string, cleanup_string,
955 costvec, ncostvec, coefficient)
956 FRAME_PTR frame;
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)
963 line_ins_del (frame,
964 string_cost (multi_string) * coefficient,
965 per_line_cost (multi_string) * coefficient,
966 0, 0, costvec, ncostvec);
967 else if (one_line_string)
968 line_ins_del (frame,
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
974 line_ins_del (frame,
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).
982 This must be redone whenever the frame height changes.
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
991 [tt.t_ILov + (frame_height + 1 - L) * tt.t_ILpf] +
992 N * [tt.t_ILnov + (frame_height + 1 - L) * tt.t_ILnpf]
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
996 L changes (frame_height + 1 - L) lines.
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
1001 FRAME_INSERT_COST (frame) and the multiply factor in
1002 FRAME_INSERTN_COST (frame). Note however that any insertion
1003 must include at least one multiply factor. Rather than compute this
1004 as FRAME_INSERT_COST (frame)[line]+FRAME_INSERTN_COST (frame)[line],
1005 we add FRAME_INSERTN_COST (frame) into FRAME_INSERT_COST (frame).
1006 This is reasonable because of the particular algorithm used in calcM.
1007
1008 Deletion is essentially the same as insertion.
1009 */
1010
1011 do_line_insertion_deletion_costs (frame,
1012 ins_line_string, multi_ins_string,
1013 del_line_string, multi_del_string,
1014 setup_string, cleanup_string, coefficient)
1015 FRAME_PTR frame;
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 {
1021 if (FRAME_INSERT_COST (frame) != 0)
1022 {
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));
1035 }
1036 else
1037 {
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));
1046 }
1047
1048 ins_del_costs (frame,
1049 ins_line_string, multi_ins_string,
1050 setup_string, cleanup_string,
1051 FRAME_INSERT_COST (frame), FRAME_INSERTN_COST (frame),
1052 coefficient);
1053 ins_del_costs (frame,
1054 del_line_string, multi_del_string,
1055 setup_string, cleanup_string,
1056 FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
1057 coefficient);
1058 }