diff options
| author | Gerd Moellmann | 1999-07-21 21:43:52 +0000 |
|---|---|---|
| committer | Gerd Moellmann | 1999-07-21 21:43:52 +0000 |
| commit | 8970011174264a7fcba68d789df30c9fead389ec (patch) | |
| tree | 73563267e877c1d9369ae564d9f9c1e2d1b56c7a /src/scroll.c | |
| parent | 7840ced19916e4443b30d00c386b6b3d8584459e (diff) | |
| download | emacs-8970011174264a7fcba68d789df30c9fead389ec.tar.gz emacs-8970011174264a7fcba68d789df30c9fead389ec.zip | |
Rewritten.
Diffstat (limited to 'src/scroll.c')
| -rw-r--r-- | src/scroll.c | 537 |
1 files changed, 275 insertions, 262 deletions
diff --git a/src/scroll.c b/src/scroll.c index 12c3828b33c..727d96442b5 100644 --- a/src/scroll.c +++ b/src/scroll.c | |||
| @@ -20,12 +20,13 @@ Boston, MA 02111-1307, USA. */ | |||
| 20 | 20 | ||
| 21 | 21 | ||
| 22 | #include <config.h> | 22 | #include <config.h> |
| 23 | #include <stdio.h> | ||
| 24 | #include <string.h> | ||
| 23 | #include "termchar.h" | 25 | #include "termchar.h" |
| 24 | #include "lisp.h" | 26 | #include "lisp.h" |
| 25 | #include "dispextern.h" | 27 | #include "dispextern.h" |
| 26 | #include "frame.h" | 28 | #include "frame.h" |
| 27 | 29 | #include "window.h" | |
| 28 | extern struct display_line **ophys_lines; | ||
| 29 | 30 | ||
| 30 | #define max(a, b) ((a) > (b) ? (a) : (b)) | 31 | #define max(a, b) ((a) > (b) ? (a) : (b)) |
| 31 | #define min(a, b) ((a) < (b) ? (a) : (b)) | 32 | #define min(a, b) ((a) < (b) ? (a) : (b)) |
| @@ -58,6 +59,13 @@ struct matrix_elt | |||
| 58 | unsigned char writecount; | 59 | unsigned char writecount; |
| 59 | }; | 60 | }; |
| 60 | 61 | ||
| 62 | static void do_direct_scrolling P_ ((struct glyph_matrix *, | ||
| 63 | struct matrix_elt *, | ||
| 64 | int, int)); | ||
| 65 | static void do_scrolling P_ ((struct glyph_matrix *, | ||
| 66 | struct matrix_elt *, | ||
| 67 | int, int)); | ||
| 68 | |||
| 61 | 69 | ||
| 62 | /* Determine, in matrix[i,j], the cost of updating the first j old | 70 | /* 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. | 71 | lines into the first i new lines using the general scrolling method. |
| @@ -96,7 +104,7 @@ calculate_scrolling (frame, matrix, window_size, lines_below, | |||
| 96 | 104 | ||
| 97 | int lines_moved = window_size + (scroll_region_ok ? 0 : lines_below); | 105 | 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 | 106 | /* 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, | 107 | at the i'th line of the lines we are considering, |
| 100 | where I is origin 1 (as it is below). */ | 108 | where I is origin 1 (as it is below). */ |
| 101 | int *first_insert_cost | 109 | int *first_insert_cost |
| 102 | = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved]; | 110 | = &FRAME_INSERT_COST (frame)[frame_height - 1 - lines_moved]; |
| @@ -220,150 +228,162 @@ calculate_scrolling (frame, matrix, window_size, lines_below, | |||
| 220 | p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1; | 228 | p->deletecount = (cost < cost1) ? 1 : p1->deletecount + 1; |
| 221 | } | 229 | } |
| 222 | } | 230 | } |
| 231 | |||
| 232 | |||
| 223 | 233 | ||
| 224 | /* Perform insert-lines and delete-lines operations on FRAME according | 234 | /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX |
| 225 | to the costs in MATRIX, using the general scrolling method. | 235 | according to the costs in MATRIX, using the general scrolling |
| 226 | Update the frame's current_glyphs info to record what was done. | 236 | method that is used if the terminal does not support the setting of |
| 237 | scroll windows (scroll_region_ok == 0). | ||
| 227 | 238 | ||
| 228 | WINDOW_SIZE is the number of lines being considered for scrolling | 239 | 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. | 240 | and UNCHANGED_AT_TOP is the vpos of the first line being |
| 230 | These two arguments can specify any contiguous range of lines. | 241 | considered. These two arguments can specify any contiguous range |
| 242 | of lines. */ | ||
| 231 | 243 | ||
| 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 | 244 | static void |
| 240 | do_scrolling (frame, matrix, window_size, unchanged_at_top) | 245 | do_scrolling (current_matrix, matrix, window_size, unchanged_at_top) |
| 241 | FRAME_PTR frame; | 246 | struct glyph_matrix *current_matrix; |
| 242 | struct matrix_elt *matrix; | 247 | struct matrix_elt *matrix; |
| 243 | int window_size; | 248 | int window_size; |
| 244 | int unchanged_at_top; | 249 | int unchanged_at_top; |
| 245 | { | 250 | { |
| 246 | register struct matrix_elt *p; | 251 | struct matrix_elt *p; |
| 247 | register int i, j; | 252 | int i, j, k; |
| 248 | register struct frame_glyphs *current_frame; | 253 | |
| 249 | /* temp_frame->enable[i] means line i has been moved to current_frame. */ | 254 | /* Set to 1 if we have set a terminal window with |
| 250 | register struct frame_glyphs *temp_frame; | 255 | set_terminal_window. */ |
| 251 | struct queue { int count, pos; } *queue; | 256 | int terminal_window_p = 0; |
| 252 | int offset = unchanged_at_top; | 257 | |
| 253 | int qi = 0; | 258 | /* A queue for line insertions to be done. */ |
| 254 | int window = 0; | 259 | struct queue { int count, pos; }; |
| 255 | register int tem; | 260 | struct queue *queue_start |
| 256 | int next; | 261 | = (struct queue *) alloca (current_matrix->nrows * sizeof (struct queue)); |
| 257 | 262 | struct queue *queue = queue_start; | |
| 258 | queue = (struct queue *) alloca (FRAME_HEIGHT (frame) | 263 | |
| 259 | * sizeof (struct queue)); | 264 | char *retained_p = (char *) alloca (window_size * sizeof (char)); |
| 260 | 265 | int *copy_from = (int *) alloca (window_size * sizeof (int)); | |
| 261 | current_frame = FRAME_CURRENT_GLYPHS (frame); | 266 | |
| 262 | temp_frame = FRAME_TEMP_GLYPHS (frame); | 267 | /* Zero means line is empty. */ |
| 263 | 268 | bzero (retained_p, window_size * sizeof (char)); | |
| 264 | bcopy (current_frame->glyphs, temp_frame->glyphs, | 269 | for (k = 0; k < window_size; ++k) |
| 265 | current_frame->height * sizeof (GLYPH *)); | 270 | copy_from[k] = -1; |
| 266 | bcopy (current_frame->charstarts, temp_frame->charstarts, | 271 | |
| 267 | current_frame->height * sizeof (int *)); | 272 | #define CHECK \ |
| 268 | bcopy (current_frame->used, temp_frame->used, | 273 | do \ |
| 269 | current_frame->height * sizeof (int)); | 274 | { \ |
| 270 | bcopy (current_frame->highlight, temp_frame->highlight, | 275 | int k; \ |
| 271 | current_frame->height * sizeof (char)); | 276 | for (k = 0; k < window_size; ++k) \ |
| 272 | bzero (temp_frame->enable, temp_frame->height * sizeof (char)); | 277 | xassert (copy_from[k] == -1 \ |
| 273 | bcopy (current_frame->bufp, temp_frame->bufp, | 278 | || (copy_from[k] >= 0 && copy_from[k] < window_size)); \ |
| 274 | current_frame->height * sizeof (int)); | 279 | } \ |
| 275 | 280 | while (0); | |
| 276 | #ifdef HAVE_WINDOW_SYSTEM | 281 | |
| 277 | if (FRAME_WINDOW_P (frame)) | 282 | /* When j is advanced, this corresponds to deleted lines. |
| 278 | { | 283 | When i is advanced, this corresponds to inserted lines. */ |
| 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; | 284 | i = j = window_size; |
| 293 | |||
| 294 | while (i > 0 || j > 0) | 285 | while (i > 0 || j > 0) |
| 295 | { | 286 | { |
| 296 | p = matrix + i * (window_size + 1) + j; | 287 | p = matrix + i * (window_size + 1) + j; |
| 297 | tem = p->insertcost; | 288 | |
| 298 | if (tem < p->writecost && tem < p->deletecost) | 289 | if (p->insertcost < p->writecost && p->insertcost < p->deletecost) |
| 299 | { | 290 | { |
| 300 | /* Insert should be done at vpos i-1, plus maybe some before */ | 291 | /* Insert should be done at vpos i-1, plus maybe some before. |
| 301 | queue[qi].count = p->insertcount; | 292 | Queue the screen operation to be performed. */ |
| 293 | queue->count = p->insertcount; | ||
| 294 | queue->pos = i + unchanged_at_top - p->insertcount; | ||
| 295 | ++queue; | ||
| 296 | |||
| 297 | /* By incrementing I, we leave room in the result rows | ||
| 298 | for the empty rows opened up. */ | ||
| 302 | i -= p->insertcount; | 299 | i -= p->insertcount; |
| 303 | queue[qi++].pos = i + unchanged_at_top; | ||
| 304 | } | 300 | } |
| 305 | else if (p->deletecost < p->writecost) | 301 | else if (p->deletecost < p->writecost) |
| 306 | { | 302 | { |
| 307 | /* Old line at vpos j-1, and maybe some before it, | 303 | /* Old line at vpos j-1, and maybe some before it, should be |
| 308 | should be deleted */ | 304 | deleted. By decrementing J, we skip some lines in the |
| 305 | temp_rows which is equivalent to omitting these lines in | ||
| 306 | the result rows, thus deleting them. */ | ||
| 309 | j -= p->deletecount; | 307 | j -= p->deletecount; |
| 310 | if (!window) | 308 | |
| 309 | /* Set the terminal window, if not done already. */ | ||
| 310 | if (! terminal_window_p) | ||
| 311 | { | 311 | { |
| 312 | set_terminal_window (window_size + unchanged_at_top); | 312 | set_terminal_window (window_size + unchanged_at_top); |
| 313 | window = 1; | 313 | terminal_window_p = 1; |
| 314 | } | 314 | } |
| 315 | |||
| 316 | /* Delete lines on the terminal. */ | ||
| 315 | ins_del_lines (j + unchanged_at_top, - p->deletecount); | 317 | ins_del_lines (j + unchanged_at_top, - p->deletecount); |
| 316 | } | 318 | } |
| 317 | else | 319 | else |
| 318 | { | 320 | { |
| 319 | /* Best thing done here is no insert or delete */ | 321 | /* Best thing done here is no insert or delete, i.e. a write. */ |
| 320 | /* Old line at vpos j-1 ends up at vpos i-1 */ | 322 | --i, --j; |
| 321 | current_frame->glyphs[i + offset - 1] | 323 | xassert (i >= 0 && i < window_size); |
| 322 | = temp_frame->glyphs[j + offset - 1]; | 324 | xassert (j >= 0 && j < window_size); |
| 323 | current_frame->charstarts[i + offset - 1] | 325 | copy_from[i] = j; |
| 324 | = temp_frame->charstarts[j + offset - 1]; | 326 | retained_p[j] = 1; |
| 325 | current_frame->used[i + offset - 1] | 327 | |
| 326 | = temp_frame->used[j + offset - 1]; | 328 | #if GLYPH_DEBUG |
| 327 | current_frame->highlight[i + offset - 1] | 329 | CHECK; |
| 328 | = temp_frame->highlight[j + offset - 1]; | 330 | #endif |
| 329 | |||
| 330 | temp_frame->enable[j + offset - 1] = 1; | ||
| 331 | i--; | ||
| 332 | j--; | ||
| 333 | } | 331 | } |
| 334 | } | 332 | } |
| 335 | 333 | ||
| 336 | if (!window && qi) | 334 | /* Now do all insertions queued above. */ |
| 335 | if (queue > queue_start) | ||
| 337 | { | 336 | { |
| 338 | set_terminal_window (window_size + unchanged_at_top); | 337 | int next = -1; |
| 339 | window = 1; | ||
| 340 | } | ||
| 341 | 338 | ||
| 342 | /* Now do all insertions */ | 339 | /* Set the terminal window if not yet done. */ |
| 340 | if (!terminal_window_p) | ||
| 341 | { | ||
| 342 | set_terminal_window (window_size + unchanged_at_top); | ||
| 343 | terminal_window_p = 1; | ||
| 344 | } | ||
| 343 | 345 | ||
| 344 | next = unchanged_at_top; | 346 | do |
| 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 | { | 347 | { |
| 356 | current_frame->enable[j] = 0; | 348 | --queue; |
| 357 | while (temp_frame->enable[next]) | 349 | |
| 358 | next++; | 350 | /* Do the deletion on the terminal. */ |
| 359 | current_frame->glyphs[j] = temp_frame->glyphs[next]; | 351 | ins_del_lines (queue->pos, queue->count); |
| 360 | current_frame->charstarts[j] = temp_frame->charstarts[next++]; | 352 | |
| 353 | /* All lines in the range deleted become empty in the glyph | ||
| 354 | matrix. Assign to them glyph rows that are not retained. | ||
| 355 | K is the starting position of the deleted range relative | ||
| 356 | to the window we are working in. */ | ||
| 357 | k = queue->pos - unchanged_at_top; | ||
| 358 | for (j = 0; j < queue->count; ++j) | ||
| 359 | { | ||
| 360 | /* Find the next row not retained. */ | ||
| 361 | while (retained_p[++next]) | ||
| 362 | ; | ||
| 363 | |||
| 364 | /* Record that this row is to be used for the empty | ||
| 365 | glyph row j. */ | ||
| 366 | copy_from[k + j] = next; | ||
| 367 | } | ||
| 361 | } | 368 | } |
| 369 | while (queue > queue_start); | ||
| 370 | |||
| 362 | } | 371 | } |
| 363 | 372 | ||
| 364 | if (window) | 373 | for (k = 0; k < window_size; ++k) |
| 374 | xassert (copy_from[k] >= 0 && copy_from[k] < window_size); | ||
| 375 | |||
| 376 | /* Perform the row swizzling. */ | ||
| 377 | mirrored_line_dance (current_matrix, unchanged_at_top, window_size, | ||
| 378 | copy_from, retained_p); | ||
| 379 | |||
| 380 | /* Some sanity checks if GLYPH_DEBUG != 0. */ | ||
| 381 | CHECK_MATRIX (current_matrix); | ||
| 382 | |||
| 383 | if (terminal_window_p) | ||
| 365 | set_terminal_window (0); | 384 | set_terminal_window (0); |
| 366 | } | 385 | } |
| 386 | |||
| 367 | 387 | ||
| 368 | /* Determine, in matrix[i,j], the cost of updating the first j | 388 | /* Determine, in matrix[i,j], the cost of updating the first j |
| 369 | old lines into the first i new lines using the direct | 389 | old lines into the first i new lines using the direct |
| @@ -611,186 +631,179 @@ calculate_direct_scrolling (frame, matrix, window_size, lines_below, | |||
| 611 | p->deletecost = cost; | 631 | p->deletecost = cost; |
| 612 | } | 632 | } |
| 613 | } | 633 | } |
| 634 | |||
| 635 | |||
| 614 | 636 | ||
| 615 | /* Perform insert-lines and delete-lines operations on FRAME according | 637 | /* Perform insert-lines and delete-lines operations on CURRENT_MATRIX |
| 616 | to the costs in MATRIX, using the direct scrolling method. | 638 | according to the costs in MATRIX, using the direct scrolling method |
| 617 | Update the frame's current_glyphs info to record what was done. | 639 | which is used when the terminal supports setting a scroll window |
| 640 | (scroll_region_ok). | ||
| 618 | 641 | ||
| 619 | WINDOW_SIZE is the number of lines being considered for scrolling | 642 | 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. | 643 | and UNCHANGED_AT_TOP is the vpos of the first line being |
| 621 | These two arguments can specify any contiguous range of lines. | 644 | considered. These two arguments can specify any contiguous range |
| 645 | of lines. | ||
| 622 | 646 | ||
| 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 | 647 | In the direct scrolling method, a new scroll window is selected |
| 631 | before each insertion or deletion, so that groups of lines can be | 648 | before each insertion or deletion, so that groups of lines can be |
| 632 | scrolled directly to their final vertical positions. This method | 649 | scrolled directly to their final vertical positions. This method |
| 633 | is described in more detail in calculate_direct_scrolling, | 650 | is described in more detail in calculate_direct_scrolling, where |
| 634 | where the cost matrix for this approach is constructed. */ | 651 | the cost matrix for this approach is constructed. */ |
| 635 | 652 | ||
| 636 | static void | 653 | static void |
| 637 | do_direct_scrolling (frame, matrix, window_size, unchanged_at_top) | 654 | do_direct_scrolling (current_matrix, cost_matrix, window_size, |
| 638 | FRAME_PTR frame; | 655 | unchanged_at_top) |
| 639 | struct matrix_elt *matrix; | 656 | struct glyph_matrix *current_matrix; |
| 657 | struct matrix_elt *cost_matrix; | ||
| 640 | int window_size; | 658 | int window_size; |
| 641 | int unchanged_at_top; | 659 | int unchanged_at_top; |
| 642 | { | 660 | { |
| 643 | register struct matrix_elt *p; | 661 | struct matrix_elt *p; |
| 644 | register int i, j; | 662 | 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 | 663 | ||
| 655 | /* A nonzero value of write_follows indicates that a write has been | 664 | /* A queue of deletions and insertions to be performed. */ |
| 656 | selected, allowing either an insert or a delete to be selected next. | 665 | struct alt_queue { int count, pos, window; }; |
| 657 | When write_follows is zero, a delete cannot be selected unless j < i, | 666 | struct alt_queue *queue_start = (struct alt_queue *) |
| 658 | and an insert cannot be selected unless i < j. This corresponds to | 667 | alloca (window_size * sizeof *queue_start); |
| 659 | a similar restriction (with the ordering reversed) in | 668 | struct alt_queue *queue = queue_start; |
| 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 | 669 | ||
| 698 | i = j = window_size; | 670 | /* Set to 1 if a terminal window has been set with |
| 671 | set_terminal_window: */ | ||
| 672 | int terminal_window_p = 0; | ||
| 699 | 673 | ||
| 674 | /* A nonzero value of write_follows indicates that a write has been | ||
| 675 | selected, allowing either an insert or a delete to be selected | ||
| 676 | next. When write_follows is zero, a delete cannot be selected | ||
| 677 | unless j < i, and an insert cannot be selected unless i < j. | ||
| 678 | This corresponds to a similar restriction (with the ordering | ||
| 679 | reversed) in calculate_direct_scrolling, which is intended to | ||
| 680 | ensure that lines marked as inserted will be blank. */ | ||
| 681 | int write_follows_p = 1; | ||
| 682 | |||
| 683 | /* For each row in the new matrix what row of the old matrix it is. */ | ||
| 684 | int *copy_from = (int *) alloca (window_size * sizeof (int)); | ||
| 685 | |||
| 686 | /* Non-zero for each row in the new matrix that is retained from the | ||
| 687 | old matrix. Lines not retained are empty. */ | ||
| 688 | char *retained_p = (char *) alloca (window_size * sizeof (char)); | ||
| 689 | |||
| 690 | bzero (retained_p, window_size * sizeof (char)); | ||
| 691 | |||
| 692 | /* Perform some sanity checks when GLYPH_DEBUG is on. */ | ||
| 693 | CHECK_MATRIX (current_matrix); | ||
| 694 | |||
| 695 | /* We are working on the line range UNCHANGED_AT_TOP ... | ||
| 696 | UNCHANGED_AT_TOP + WINDOW_SIZE (not including) in CURRENT_MATRIX. | ||
| 697 | We step through lines in this range from the end to the start. I | ||
| 698 | is an index into new lines, j an index into old lines. The cost | ||
| 699 | matrix determines what to do for ranges of indices. | ||
| 700 | |||
| 701 | If i is decremented without also decrementing j, this corresponds | ||
| 702 | to inserting empty lines in the result. If j is decremented | ||
| 703 | without also decrementing i, this corresponds to omitting these | ||
| 704 | lines in the new rows, i.e. rows are deleted. */ | ||
| 705 | i = j = window_size; | ||
| 706 | |||
| 700 | while (i > 0 || j > 0) | 707 | while (i > 0 || j > 0) |
| 701 | { | 708 | { |
| 702 | p = matrix + i * (window_size + 1) + j; | 709 | p = cost_matrix + i * (window_size + 1) + j; |
| 703 | tem = p->insertcost; | 710 | |
| 704 | if (tem < p->writecost && tem < p->deletecost | 711 | if (p->insertcost < p->writecost |
| 705 | && (write_follows || i < j)) | 712 | && p->insertcost < p->deletecost |
| 713 | && (write_follows_p || i < j)) | ||
| 706 | { | 714 | { |
| 707 | /* Insert should be done at vpos i-1, plus maybe some before. | 715 | /* Insert is cheaper than deleting or writing lines. Leave |
| 708 | Setting count to 0 in the queue marks this as an insert. */ | 716 | a hole in the result display that will be filled with |
| 709 | write_follows = 0; | 717 | empty lines when the queue is emptied. */ |
| 710 | queue[qi].window = i + unchanged_at_top; | 718 | queue->count = 0; |
| 711 | queue[qi].count = 0; | 719 | queue->window = i; |
| 720 | queue->pos = i - p->insertcount; | ||
| 721 | ++queue; | ||
| 722 | |||
| 712 | i -= p->insertcount; | 723 | i -= p->insertcount; |
| 713 | queue[qi++].pos = i + unchanged_at_top; | 724 | write_follows_p = 0; |
| 714 | } | 725 | } |
| 715 | else if (p->deletecost < p->writecost && (write_follows || i > j)) | 726 | else if (p->deletecost < p->writecost |
| 727 | && (write_follows_p || i > j)) | ||
| 716 | { | 728 | { |
| 717 | /* Delete should be done at vpos j-1, plus maybe some before. */ | 729 | /* Deleting lines is cheaper. By decrementing J, omit |
| 718 | write_follows = 0; | 730 | deletecount lines from the original. */ |
| 731 | write_follows_p = 0; | ||
| 719 | j -= p->deletecount; | 732 | j -= p->deletecount; |
| 720 | } | 733 | } |
| 721 | else | 734 | else |
| 722 | { | 735 | { |
| 723 | /* One or more lines should be retained. */ | 736 | /* One or more lines should be written. In the direct |
| 724 | write_follows = 1; | 737 | scrolling method we do this by scrolling the lines to the |
| 725 | tem = p->writecount; | 738 | place they belong. */ |
| 739 | int n_to_write = p->writecount; | ||
| 740 | write_follows_p = 1; | ||
| 741 | xassert (n_to_write > 0); | ||
| 742 | |||
| 726 | if (i > j) | 743 | if (i > j) |
| 727 | { | 744 | { |
| 728 | /* Immediately scroll a group of lines downward */ | 745 | /* Immediately insert lines */ |
| 729 | set_terminal_window (i + unchanged_at_top); | 746 | set_terminal_window (i + unchanged_at_top); |
| 730 | window = 1; | 747 | terminal_window_p = 1; |
| 731 | ins_del_lines (j - tem + unchanged_at_top, i - j); | 748 | ins_del_lines (j - n_to_write + unchanged_at_top, i - j); |
| 732 | } | 749 | } |
| 733 | else if (i < j) | 750 | else if (i < j) |
| 734 | { | 751 | { |
| 735 | /* Queue the upward scrolling of a group of lines */ | 752 | /* Queue the deletion of a group of lines */ |
| 736 | queue[qi].pos = i - tem + unchanged_at_top; | 753 | queue->pos = i - n_to_write + unchanged_at_top; |
| 737 | queue[qi].window = j + unchanged_at_top; | 754 | queue->window = j + unchanged_at_top; |
| 738 | queue[qi++].count = i - j; | 755 | queue->count = i - j; |
| 756 | ++queue; | ||
| 739 | } | 757 | } |
| 740 | 758 | ||
| 741 | /* Now copy the line-contents strings */ | 759 | while (n_to_write > 0) |
| 742 | while (tem > 0) | ||
| 743 | { | 760 | { |
| 744 | i--; | 761 | --i, --j, --n_to_write; |
| 745 | j--; | 762 | copy_from[i] = j; |
| 746 | tem--; | 763 | retained_p[j] = 1; |
| 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 | } | 764 | } |
| 758 | } | 765 | } |
| 759 | } | 766 | } |
| 760 | 767 | ||
| 761 | /* Now do all the upward scrolling, and copy the line-contents | 768 | /* Do queued operations. */ |
| 762 | strings for the blank lines of the recorded inserts. */ | 769 | if (queue > queue_start) |
| 763 | |||
| 764 | next = unchanged_at_top; | ||
| 765 | for (i = qi - 1; i >= 0; i--) | ||
| 766 | { | 770 | { |
| 767 | if (queue[i].count) | 771 | int next = -1; |
| 768 | { | 772 | |
| 769 | set_terminal_window (queue[i].window); | 773 | do |
| 770 | window = 1; | ||
| 771 | ins_del_lines (queue[i].pos, queue[i].count); | ||
| 772 | } | ||
| 773 | else | ||
| 774 | { | 774 | { |
| 775 | /* Mark the inserted lines as clear, | 775 | --queue; |
| 776 | and put into them the line-contents strings | 776 | if (queue->count) |
| 777 | that were discarded during the deletions. | 777 | { |
| 778 | Those are the ones for which temp_frame->enable was not set. */ | 778 | set_terminal_window (queue->window); |
| 779 | tem = queue[i].pos; | 779 | terminal_window_p = 1; |
| 780 | for (j = queue[i].window - 1; j >= tem; j--) | 780 | ins_del_lines (queue->pos, queue->count); |
| 781 | } | ||
| 782 | else | ||
| 781 | { | 783 | { |
| 782 | current_frame->enable[j] = 0; | 784 | for (j = queue->window - 1; j >= queue->pos; --j) |
| 783 | while (temp_frame->enable[next]) | 785 | { |
| 784 | next++; | 786 | while (retained_p[++next]) |
| 785 | current_frame->glyphs[j] = temp_frame->glyphs[next]; | 787 | ; |
| 786 | current_frame->charstarts[j] = temp_frame->charstarts[next++]; | 788 | copy_from[j] = next; |
| 789 | } | ||
| 787 | } | 790 | } |
| 788 | } | 791 | } |
| 792 | while (queue > queue_start); | ||
| 789 | } | 793 | } |
| 790 | 794 | ||
| 791 | if (window) | 795 | /* Now, for each row I in the range of rows we are working on, |
| 796 | copy_from[i] gives the original line to copy to I, and | ||
| 797 | retained_p[copy_from[i]] is zero if line I in the new display is | ||
| 798 | empty. */ | ||
| 799 | mirrored_line_dance (current_matrix, unchanged_at_top, window_size, | ||
| 800 | copy_from, retained_p); | ||
| 801 | |||
| 802 | if (terminal_window_p) | ||
| 792 | set_terminal_window (0); | 803 | set_terminal_window (0); |
| 793 | } | 804 | } |
| 805 | |||
| 806 | |||
| 794 | 807 | ||
| 795 | void | 808 | void |
| 796 | scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom, | 809 | scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom, |
| @@ -813,22 +826,26 @@ scrolling_1 (frame, window_size, unchanged_at_top, unchanged_at_bottom, | |||
| 813 | unchanged_at_bottom, | 826 | unchanged_at_bottom, |
| 814 | draw_cost, old_draw_cost, | 827 | draw_cost, old_draw_cost, |
| 815 | old_hash, new_hash, free_at_end); | 828 | old_hash, new_hash, free_at_end); |
| 816 | do_direct_scrolling (frame, matrix, window_size, unchanged_at_top); | 829 | do_direct_scrolling (frame->current_matrix, |
| 830 | matrix, window_size, unchanged_at_top); | ||
| 817 | } | 831 | } |
| 818 | else | 832 | else |
| 819 | { | 833 | { |
| 820 | calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom, | 834 | calculate_scrolling (frame, matrix, window_size, unchanged_at_bottom, |
| 821 | draw_cost, old_hash, new_hash, | 835 | draw_cost, old_hash, new_hash, |
| 822 | free_at_end); | 836 | free_at_end); |
| 823 | do_scrolling (frame, matrix, window_size, unchanged_at_top); | 837 | do_scrolling (frame->current_matrix, matrix, window_size, |
| 838 | unchanged_at_top); | ||
| 824 | } | 839 | } |
| 825 | } | 840 | } |
| 841 | |||
| 842 | |||
| 826 | 843 | ||
| 827 | /* Return number of lines in common between current and desired frame contents | 844 | /* Return number of lines in common between current and desired frame |
| 828 | described to us only as vectors of hash codes OLDHASH and NEWHASH. | 845 | contents described to us only as vectors of hash codes OLDHASH and |
| 829 | Consider only vpos range START to END (not including END). | 846 | NEWHASH. Consider only vpos range START to END (not including |
| 830 | Ignore short lines on the assumption that | 847 | END). Ignore short lines on the assumption that avoiding redrawing |
| 831 | avoiding redrawing such a line will have little weight. */ | 848 | such a line will have little weight. */ |
| 832 | 849 | ||
| 833 | int | 850 | int |
| 834 | scrolling_max_lines_saved (start, end, oldhash, newhash, cost) | 851 | scrolling_max_lines_saved (start, end, oldhash, newhash, cost) |
| @@ -851,11 +868,9 @@ scrolling_max_lines_saved (start, end, oldhash, newhash, cost) | |||
| 851 | 868 | ||
| 852 | bzero (lines, sizeof lines); | 869 | bzero (lines, sizeof lines); |
| 853 | 870 | ||
| 854 | /* Put new lines' hash codes in hash table. | 871 | /* Put new lines' hash codes in hash table. Ignore lines shorter |
| 855 | Ignore lines shorter than the threshold. | 872 | than the threshold. Thus, if the lines that are in common are |
| 856 | Thus, if the lines that are in common | 873 | mainly the ones that are short, they won't count. */ |
| 857 | are mainly the ones that are short, | ||
| 858 | they won't count. */ | ||
| 859 | for (i = start; i < end; i++) | 874 | for (i = start; i < end; i++) |
| 860 | { | 875 | { |
| 861 | if (cost[i] > threshold) | 876 | if (cost[i] > threshold) |
| @@ -866,9 +881,8 @@ scrolling_max_lines_saved (start, end, oldhash, newhash, cost) | |||
| 866 | } | 881 | } |
| 867 | } | 882 | } |
| 868 | 883 | ||
| 869 | /* Look up old line hash codes in the hash table. | 884 | /* Look up old line hash codes in the hash table. Count number of |
| 870 | Count number of matches between old lines and new. */ | 885 | matches between old lines and new. */ |
| 871 | |||
| 872 | for (i = start; i < end; i++) | 886 | for (i = start; i < end; i++) |
| 873 | { | 887 | { |
| 874 | h = oldhash[i] & 0777; | 888 | h = oldhash[i] & 0777; |
| @@ -883,11 +897,10 @@ scrolling_max_lines_saved (start, end, oldhash, newhash, cost) | |||
| 883 | return matchcount; | 897 | return matchcount; |
| 884 | } | 898 | } |
| 885 | 899 | ||
| 886 | /* Return a measure of the cost of moving the lines | 900 | /* Return a measure of the cost of moving the lines starting with vpos |
| 887 | starting with vpos FROM, up to but not including vpos TO, | 901 | FROM, up to but not including vpos TO, down by AMOUNT lines (AMOUNT |
| 888 | down by AMOUNT lines (AMOUNT may be negative). | 902 | may be negative). These are the same arguments that might be given |
| 889 | These are the same arguments that might be given to | 903 | to scroll_frame_lines to perform this scrolling. */ |
| 890 | scroll_frame_lines to perform this scrolling. */ | ||
| 891 | 904 | ||
| 892 | int | 905 | int |
| 893 | scroll_cost (frame, from, to, amount) | 906 | scroll_cost (frame, from, to, amount) |