1 /* display.c -- How to display Info windows. 2 $Id: display.c,v 1.2 1999/01/11 16:38:06 espie Exp $ 3 4 Copyright (C) 1993, 97 Free Software Foundation, Inc. 5 6 This program 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 This program 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 this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20 Written by Brian Fox (bfox@ai.mit.edu). */ 21 22 #include "info.h" 23 #include "display.h" 24 25 extern int info_any_buffered_input_p (); /* Found in session.c. */ 26 27 static void free_display (); 28 static DISPLAY_LINE **make_display (); 29 30 /* An array of display lines which tell us what is currently visible on 31 the display. */ 32 DISPLAY_LINE **the_display = (DISPLAY_LINE **)NULL; 33 34 /* Non-zero means do no output. */ 35 int display_inhibited = 0; 36 37 /* Initialize THE_DISPLAY to WIDTH and HEIGHT, with nothing in it. */ 38 void 39 display_initialize_display (width, height) 40 int width, height; 41 { 42 free_display (the_display); 43 the_display = make_display (width, height); 44 display_clear_display (the_display); 45 } 46 47 /* Clear all of the lines in DISPLAY making the screen blank. */ 48 void 49 display_clear_display (display) 50 DISPLAY_LINE **display; 51 { 52 register int i; 53 register DISPLAY_LINE *display_line; 54 55 for (i = 0; (display_line = display[i]); i++) 56 { 57 display[i]->text[0] = '\0'; 58 display[i]->textlen = 0; 59 display[i]->inverse = 0; 60 } 61 } 62 63 /* Non-zero if we didn't completely redisplay a window. */ 64 int display_was_interrupted_p = 0; 65 66 /* Update the windows pointed to by WINDOW in the_display. This actually 67 writes the text on the screen. */ 68 void 69 display_update_display (window) 70 WINDOW *window; 71 { 72 register WINDOW *win; 73 74 display_was_interrupted_p = 0; 75 76 /* For every window in the list, check contents against the display. */ 77 for (win = window; win; win = win->next) 78 { 79 /* Only re-display visible windows which need updating. */ 80 if (((win->flags & W_WindowVisible) == 0) || 81 ((win->flags & W_UpdateWindow) == 0) || 82 (win->height == 0)) 83 continue; 84 85 display_update_one_window (win); 86 if (display_was_interrupted_p) 87 break; 88 } 89 90 /* Always update the echo area. */ 91 display_update_one_window (the_echo_area); 92 } 93 94 /* Display WIN on the_display. Unlike display_update_display (), this 95 function only does one window. */ 96 void 97 display_update_one_window (win) 98 WINDOW *win; 99 { 100 register char *nodetext; /* Current character to display. */ 101 register char *last_node_char; /* Position of the last character in node. */ 102 register int i; /* General use index. */ 103 char *printed_line; /* Buffer for a printed line. */ 104 int pl_index = 0; /* Index into PRINTED_LINE. */ 105 int line_index = 0; /* Number of lines done so far. */ 106 DISPLAY_LINE **display = the_display; 107 108 /* If display is inhibited, that counts as an interrupted display. */ 109 if (display_inhibited) 110 display_was_interrupted_p = 1; 111 112 /* If the window has no height, or display is inhibited, quit now. */ 113 if (!win->height || display_inhibited) 114 return; 115 116 /* If the window's first row doesn't appear in the_screen, then it 117 cannot be displayed. This can happen when the_echo_area is the 118 window to be displayed, and the screen has shrunk to less than one 119 line. */ 120 if ((win->first_row < 0) || (win->first_row > the_screen->height)) 121 return; 122 123 /* Print each line in the window into our local buffer, and then 124 check the contents of that buffer against the display. If they 125 differ, update the display. */ 126 printed_line = (char *)xmalloc (1 + win->width); 127 128 if (!win->node || !win->line_starts) 129 goto done_with_node_display; 130 131 nodetext = win->line_starts[win->pagetop]; 132 last_node_char = win->node->contents + win->node->nodelen; 133 134 for (; nodetext < last_node_char; nodetext++) 135 { 136 char *rep, *rep_carried_over, rep_temp[2]; 137 int replen; 138 139 if (isprint (*nodetext)) 140 { 141 rep_temp[0] = *nodetext; 142 replen = 1; 143 rep_temp[1] = '\0'; 144 rep = rep_temp; 145 } 146 else 147 { 148 if (*nodetext == '\r' || *nodetext == '\n') 149 { 150 replen = win->width - pl_index; 151 } 152 else 153 { 154 rep = printed_representation (*nodetext, pl_index); 155 replen = strlen (rep); 156 } 157 } 158 159 /* If this character can be printed without passing the width of 160 the line, then stuff it into the line. */ 161 if (replen + pl_index < win->width) 162 { 163 /* Optimize if possible. */ 164 if (replen == 1) 165 { 166 printed_line[pl_index++] = *rep; 167 } 168 else 169 { 170 for (i = 0; i < replen; i++) 171 printed_line[pl_index++] = rep[i]; 172 } 173 } 174 else 175 { 176 DISPLAY_LINE *entry; 177 178 /* If this character cannot be printed in this line, we have 179 found the end of this line as it would appear on the screen. 180 Carefully print the end of the line, and then compare. */ 181 if (*nodetext == '\n' || *nodetext == '\r' || *nodetext == '\t') 182 { 183 printed_line[pl_index] = '\0'; 184 rep_carried_over = (char *)NULL; 185 } 186 else 187 { 188 /* The printed representation of this character extends into 189 the next line. Remember the offset of the last character 190 printed out of REP so that we can carry the character over 191 to the next line. */ 192 for (i = 0; pl_index < (win->width - 1);) 193 printed_line[pl_index++] = rep[i++]; 194 195 rep_carried_over = rep + i; 196 197 /* If printing the last character in this window couldn't 198 possibly cause the screen to scroll, place a backslash 199 in the rightmost column. */ 200 if (1 + line_index + win->first_row < the_screen->height) 201 { 202 if (win->flags & W_NoWrap) 203 printed_line[pl_index++] = '$'; 204 else 205 printed_line[pl_index++] = '\\'; 206 } 207 printed_line[pl_index] = '\0'; 208 } 209 210 /* We have the exact line as it should appear on the screen. 211 Check to see if this line matches the one already appearing 212 on the screen. */ 213 entry = display[line_index + win->first_row]; 214 215 /* If the screen line is inversed, then we have to clear 216 the line from the screen first. Why, I don't know. */ 217 if (entry->inverse) 218 { 219 terminal_goto_xy (0, line_index + win->first_row); 220 terminal_clear_to_eol (); 221 entry->inverse = 0; 222 entry->text[0] = '\0'; 223 entry->textlen = 0; 224 } 225 226 /* Find the offset where these lines differ. */ 227 for (i = 0; i < pl_index; i++) 228 if (printed_line[i] != entry->text[i]) 229 break; 230 231 /* If the lines are not the same length, or if they differed 232 at all, we must do some redrawing. */ 233 if ((i != pl_index) || (pl_index != entry->textlen)) 234 { 235 /* Move to the proper point on the terminal. */ 236 terminal_goto_xy (i, line_index + win->first_row); 237 238 /* If there is any text to print, print it. */ 239 if (i != pl_index) 240 terminal_put_text (printed_line + i); 241 242 /* If the printed text didn't extend all the way to the edge 243 of the window, and text was appearing between here and the 244 edge of the window, clear from here to the end of the line. */ 245 if ((pl_index < win->width && pl_index < entry->textlen) || 246 (entry->inverse)) 247 terminal_clear_to_eol (); 248 249 fflush (stdout); 250 251 /* Update the display text buffer. */ 252 strcpy (entry->text + i, printed_line + i); 253 entry->textlen = pl_index; 254 255 /* Lines showing node text are not in inverse. Only modelines 256 have that distinction. */ 257 entry->inverse = 0; 258 } 259 260 /* We have done at least one line. Increment our screen line 261 index, and check against the bottom of the window. */ 262 if (++line_index == win->height) 263 break; 264 265 /* A line has been displayed, and the screen reflects that state. 266 If there is typeahead pending, then let that typeahead be read 267 now, instead of continuing with the display. */ 268 if (info_any_buffered_input_p ()) 269 { 270 free (printed_line); 271 display_was_interrupted_p = 1; 272 return; 273 } 274 275 /* Reset PL_INDEX to the start of the line. */ 276 pl_index = 0; 277 278 /* If there are characters from REP left to print, stuff them 279 into the buffer now. */ 280 if (rep_carried_over) 281 for (; rep[pl_index]; pl_index++) 282 printed_line[pl_index] = rep[pl_index]; 283 284 /* If this window has chosen not to wrap lines, skip to the end 285 of the physical line in the buffer, and start a new line here. */ 286 if (pl_index && (win->flags & W_NoWrap)) 287 { 288 char *begin; 289 290 pl_index = 0; 291 printed_line[0] = '\0'; 292 293 begin = nodetext; 294 295 while ((nodetext < last_node_char) && (*nodetext != '\n')) 296 nodetext++; 297 } 298 } 299 } 300 301 done_with_node_display: 302 /* We have reached the end of the node or the end of the window. If it 303 is the end of the node, then clear the lines of the window from here 304 to the end of the window. */ 305 for (; line_index < win->height; line_index++) 306 { 307 DISPLAY_LINE *entry = display[line_index + win->first_row]; 308 309 /* If this line has text on it then make it go away. */ 310 if (entry && entry->textlen) 311 { 312 entry->textlen = 0; 313 entry->text[0] = '\0'; 314 315 terminal_goto_xy (0, line_index + win->first_row); 316 terminal_clear_to_eol (); 317 } 318 } 319 320 /* Finally, if this window has a modeline it might need to be redisplayed. 321 Check the window's modeline against the one in the display, and update 322 if necessary. */ 323 if ((win->flags & W_InhibitMode) == 0) 324 { 325 window_make_modeline (win); 326 line_index = win->first_row + win->height; 327 328 /* This display line must both be in inverse, and have the same 329 contents. */ 330 if ((!display[line_index]->inverse) || 331 (strcmp (display[line_index]->text, win->modeline) != 0)) 332 { 333 terminal_goto_xy (0, line_index); 334 terminal_begin_inverse (); 335 terminal_put_text (win->modeline); 336 terminal_end_inverse (); 337 strcpy (display[line_index]->text, win->modeline); 338 display[line_index]->inverse = 1; 339 display[line_index]->textlen = strlen (win->modeline); 340 fflush (stdout); 341 } 342 } 343 344 /* Okay, this window doesn't need updating anymore. */ 345 win->flags &= ~W_UpdateWindow; 346 free (printed_line); 347 fflush (stdout); 348 } 349 350 /* Scroll the region of the_display starting at START, ending at END, and 351 moving the lines AMOUNT lines. If AMOUNT is less than zero, the lines 352 are moved up in the screen, otherwise down. Actually, it is possible 353 for no scrolling to take place in the case that the terminal doesn't 354 support it. This doesn't matter to us. */ 355 void 356 display_scroll_display (start, end, amount) 357 int start, end, amount; 358 { 359 register int i, last; 360 DISPLAY_LINE *temp; 361 362 /* If this terminal cannot do scrolling, give up now. */ 363 if (!terminal_can_scroll) 364 return; 365 366 /* If there isn't anything displayed on the screen because it is too 367 small, quit now. */ 368 if (!the_display[0]) 369 return; 370 371 /* If there is typeahead pending, then don't actually do any scrolling. */ 372 if (info_any_buffered_input_p ()) 373 return; 374 375 /* Do it on the screen. */ 376 terminal_scroll_terminal (start, end, amount); 377 378 /* Now do it in the display buffer so our contents match the screen. */ 379 if (amount > 0) 380 { 381 last = end + amount; 382 383 /* Shift the lines to scroll right into place. */ 384 for (i = 0; i < (end - start); i++) 385 { 386 temp = the_display[last - i]; 387 the_display[last - i] = the_display[end - i]; 388 the_display[end - i] = temp; 389 } 390 391 /* The lines have been shifted down in the buffer. Clear all of the 392 lines that were vacated. */ 393 for (i = start; i != (start + amount); i++) 394 { 395 the_display[i]->text[0] = '\0'; 396 the_display[i]->textlen = 0; 397 the_display[i]->inverse = 0; 398 } 399 } 400 401 if (amount < 0) 402 { 403 last = start + amount; 404 for (i = 0; i < (end - start); i++) 405 { 406 temp = the_display[last + i]; 407 the_display[last + i] = the_display[start + i]; 408 the_display[start + i] = temp; 409 } 410 411 /* The lines have been shifted up in the buffer. Clear all of the 412 lines that are left over. */ 413 for (i = end + amount; i != end; i++) 414 { 415 the_display[i]->text[0] = '\0'; 416 the_display[i]->textlen = 0; 417 the_display[i]->inverse = 0; 418 } 419 } 420 } 421 422 /* Try to scroll lines in WINDOW. OLD_PAGETOP is the pagetop of WINDOW before 423 having had its line starts recalculated. OLD_STARTS is the list of line 424 starts that used to appear in this window. OLD_COUNT is the number of lines 425 that appear in the OLD_STARTS array. */ 426 void 427 display_scroll_line_starts (window, old_pagetop, old_starts, old_count) 428 WINDOW *window; 429 int old_pagetop, old_count; 430 char **old_starts; 431 { 432 register int i, old, new; /* Indices into the line starts arrays. */ 433 int last_new, last_old; /* Index of the last visible line. */ 434 int old_first, new_first; /* Index of the first changed line. */ 435 int unchanged_at_top = 0; 436 int already_scrolled = 0; 437 438 /* Locate the first line which was displayed on the old window. */ 439 old_first = old_pagetop; 440 new_first = window->pagetop; 441 442 /* Find the last line currently visible in this window. */ 443 last_new = window->pagetop + (window->height - 1); 444 if (last_new > window->line_count) 445 last_new = window->line_count - 1; 446 447 /* Find the last line which used to be currently visible in this window. */ 448 last_old = old_pagetop + (window->height - 1); 449 if (last_old > old_count) 450 last_old = old_count - 1; 451 452 for (old = old_first, new = new_first; 453 old < last_old && new < last_new; 454 old++, new++) 455 if (old_starts[old] != window->line_starts[new]) 456 break; 457 else 458 unchanged_at_top++; 459 460 /* Loop through the old lines looking for a match in the new lines. */ 461 for (old = old_first + unchanged_at_top; old < last_old; old++) 462 { 463 for (new = new_first; new < last_new; new++) 464 if (old_starts[old] == window->line_starts[new]) 465 { 466 /* Find the extent of the matching lines. */ 467 for (i = 0; (old + i) < last_old; i++) 468 if (old_starts[old + i] != window->line_starts[new + i]) 469 break; 470 471 /* Scroll these lines if there are enough of them. */ 472 { 473 int start, end, amount; 474 475 start = (window->first_row 476 + ((old + already_scrolled) - old_pagetop)); 477 amount = new - (old + already_scrolled); 478 end = window->first_row + window->height; 479 480 /* If we are shifting the block of lines down, then the last 481 AMOUNT lines will become invisible. Thus, don't bother 482 scrolling them. */ 483 if (amount > 0) 484 end -= amount; 485 486 if ((end - start) > 0) 487 { 488 display_scroll_display (start, end, amount); 489 490 /* Some lines have been scrolled. Simulate the scrolling 491 by offsetting the value of the old index. */ 492 old += i; 493 already_scrolled += amount; 494 } 495 } 496 } 497 } 498 } 499 500 /* Move the screen cursor to directly over the current character in WINDOW. */ 501 void 502 display_cursor_at_point (window) 503 WINDOW *window; 504 { 505 int vpos, hpos; 506 507 vpos = window_line_of_point (window) - window->pagetop + window->first_row; 508 hpos = window_get_cursor_column (window); 509 terminal_goto_xy (hpos, vpos); 510 fflush (stdout); 511 } 512 513 /* **************************************************************** */ 514 /* */ 515 /* Functions Static to this File */ 516 /* */ 517 /* **************************************************************** */ 518 519 /* Make a DISPLAY_LINE ** with width and height. */ 520 static DISPLAY_LINE ** 521 make_display (width, height) 522 int width, height; 523 { 524 register int i; 525 DISPLAY_LINE **display; 526 527 display = (DISPLAY_LINE **)xmalloc ((1 + height) * sizeof (DISPLAY_LINE *)); 528 529 for (i = 0; i < height; i++) 530 { 531 display[i] = (DISPLAY_LINE *)xmalloc (sizeof (DISPLAY_LINE)); 532 display[i]->text = (char *)xmalloc (1 + width); 533 display[i]->textlen = 0; 534 display[i]->inverse = 0; 535 } 536 display[i] = (DISPLAY_LINE *)NULL; 537 return (display); 538 } 539 540 /* Free the storage allocated to DISPLAY. */ 541 static void 542 free_display (display) 543 DISPLAY_LINE **display; 544 { 545 register int i; 546 register DISPLAY_LINE *display_line; 547 548 if (!display) 549 return; 550 551 for (i = 0; (display_line = display[i]); i++) 552 { 553 free (display_line->text); 554 free (display_line); 555 } 556 free (display); 557 } 558