|
|
1.1 ! root 1: /* Newly written part of redisplay code. ! 2: Copyright (C) 1985 Richard M. Stallman. ! 3: ! 4: This file is part of GNU Emacs. ! 5: ! 6: GNU Emacs is distributed in the hope that it will be useful, ! 7: but WITHOUT ANY WARRANTY. No author or distributor ! 8: accepts responsibility to anyone for the consequences of using it ! 9: or for whether it serves any particular purpose or works at all, ! 10: unless he says so in writing. Refer to the GNU Emacs General Public ! 11: License for full details. ! 12: ! 13: Everyone is granted permission to copy, modify and redistribute ! 14: GNU Emacs, but only under the conditions described in the ! 15: GNU Emacs General Public License. A copy of this license is ! 16: supposed to have been given to you along with GNU Emacs so you ! 17: can know your rights and responsibilities. It should be in a ! 18: file named COPYING. Among other things, the copyright notice ! 19: and this notice must be preserved on all copies. */ ! 20: ! 21: ! 22: #include <signal.h> ! 23: ! 24: #include "config.h" ! 25: #include <stdio.h> ! 26: ! 27: #ifdef HAVE_TIMEVAL ! 28: #ifdef HPUX ! 29: #include <time.h> ! 30: #else ! 31: #include <sys/time.h> ! 32: #endif ! 33: #endif ! 34: ! 35: #ifdef USG ! 36: #include <termio.h> ! 37: #else /* not USG */ ! 38: #include <sys/ioctl.h> ! 39: #endif /* not USG */ ! 40: ! 41: #undef NULL ! 42: ! 43: #include "termchar.h" ! 44: #include "termopts.h" ! 45: #include "cm.h" ! 46: #include "dispextern.h" ! 47: #include "lisp.h" ! 48: #include "buffer.h" ! 49: #include "window.h" ! 50: #include "commands.h" ! 51: ! 52: #define max(a, b) ((a) > (b) ? (a) : (b)) ! 53: #define min(a, b) ((a) < (b) ? (a) : (b)) ! 54: ! 55: /* Nonzero means do not assume anything about current ! 56: contents of actual terminal screen */ ! 57: ! 58: int screen_garbaged; ! 59: ! 60: /* Desired terminal cursor position (to show position of point), ! 61: origin zero */ ! 62: ! 63: int cursX, cursY; ! 64: ! 65: /* Nonzero means last display completed and cursor is really at cursX, cursY. ! 66: Zero means it was preempted. */ ! 67: ! 68: int display_completed; ! 69: ! 70: int visible_bell; /* If true and the terminal will support it ! 71: then the screen will flash instead of ! 72: feeping when an error occurs */ ! 73: int inverse_video; /* If true and the terminal will support it ! 74: then we will use inverse video */ ! 75: ! 76: int baud_rate; /* Terminal speed, so we can calculate ! 77: the number of characters required to ! 78: make the cursor sit still for n secs. */ ! 79: ! 80: /* the current (physical) screen */ ! 81: struct display_line *PhysScreen[MScreenLength + 1]; ! 82: ! 83: /* temporary Copy of PhysScreen made in update_screen */ ! 84: struct display_line *OPhysScreen[MScreenLength + 1]; ! 85: ! 86: /* the desired (virtual) screen */ ! 87: struct display_line *DesiredScreen[MScreenLength + 1]; ! 88: ! 89: /* Record here all the display line objects, for debugging. */ ! 90: static struct display_line *all_lines[2 * MScreenLength]; ! 91: ! 92: FILE *termscript; /* Stdio stream being used for copy of all kbdinput. */ ! 93: ! 94: struct cm Wcm; /* Structure for info on cursor positioning */ ! 95: ! 96: extern short ospeed; /* Output speed (from sg_ospeed) */ ! 97: ! 98: /* Use these to chain together free lines */ ! 99: ! 100: #define LINE_NEXT(l) (*(struct display_line **) l) ! 101: #define SET_LINE_NEXT(l, next) (*((struct display_line **) l) = next) ! 102: ! 103: /* Chain of free display_line structures, chained thru LINE_NEXT. */ ! 104: ! 105: struct display_line *free_display_lines; ! 106: ! 107: /* Number of lines now free. */ ! 108: ! 109: int free_line_count; ! 110: ! 111: /* Allocate as many display_line structures ! 112: as we are ever supposed to need. ! 113: Called at startup, and also if screen size is changed. */ ! 114: ! 115: make_display_lines () ! 116: { ! 117: register int i; ! 118: register struct display_line *p, *p1; ! 119: ! 120: /* First, free any that are already allocated */ ! 121: ! 122: for (p = free_display_lines; p;) ! 123: { ! 124: p1 = p; ! 125: p = LINE_NEXT (p); ! 126: free (p1); ! 127: } ! 128: free_display_lines = 0; ! 129: free_line_count = 0; ! 130: ! 131: for (i = 0; i <= MScreenLength; i++) ! 132: if (PhysScreen[i]) ! 133: { ! 134: free (PhysScreen[i]); ! 135: PhysScreen[i] = 0; ! 136: } ! 137: ! 138: screen_garbaged = 1; ! 139: ! 140: /* Now allocate as many as we can possibly validly need */ ! 141: ! 142: for (i = - screen_height; i < screen_height; i++) ! 143: { ! 144: p = (struct display_line *) malloc (sizeof (struct display_line) + screen_width - MScreenWidth); ! 145: if (!p) abort (); ! 146: SET_LINE_NEXT (p, free_display_lines); ! 147: free_display_lines = p; ! 148: all_lines[i + screen_height] = p; ! 149: } ! 150: free_line_count = 2 * screen_height; ! 151: } ! 152: ! 153: /* Get one of the previously malloc'd display_line structures ! 154: from the free pool. */ ! 155: ! 156: struct display_line * ! 157: new_display_line () ! 158: { ! 159: register struct display_line *p = free_display_lines; ! 160: /* If we ever use up all the display lines that have been ! 161: allocated, it indicates a bug, since we are supposed ! 162: to need at most two for each line on the screen. */ ! 163: if (!p) ! 164: abort (); ! 165: free_display_lines = LINE_NEXT (p); ! 166: ! 167: bzero (p, p->body - (char *) p); ! 168: SET_LINE_NEXT (p, (struct display_line *)1); /* Mark as in use. */ ! 169: free_line_count--; ! 170: return p; ! 171: } ! 172: ! 173: /* Put a display_line back in the free pool. */ ! 174: ! 175: return_display_line (p) ! 176: struct display_line *p; ! 177: { ! 178: if (!p) ! 179: return; ! 180: if ((int) LINE_NEXT (p) != 1) ! 181: abort (); /* Already free. */ ! 182: SET_LINE_NEXT (p, free_display_lines); ! 183: free_display_lines = p; ! 184: free_line_count++; ! 185: } ! 186: ! 187: clear_screen_records () ! 188: { ! 189: register int i; ! 190: for (i = 1; i <= screen_height; i++) ! 191: if (PhysScreen[i]) ! 192: return_display_line (PhysScreen[i]); ! 193: bzero (PhysScreen, (screen_height + 1) * sizeof PhysScreen[0]); ! 194: } ! 195: ! 196: /* Return the hash code of display_line p. */ ! 197: line_hash_code (p) ! 198: register struct display_line *p; ! 199: { ! 200: register char *body, *end; ! 201: register int h = 0; ! 202: if (!p) ! 203: return 0; ! 204: /* Give all lighlighted lines the same hash code ! 205: so as to encourage scrolling to leave them in place. */ ! 206: if (p->highlighted) ! 207: return -1; ! 208: ! 209: body = p->body; ! 210: end = body + p->length; ! 211: *end = 0; ! 212: if (!must_write_spaces) ! 213: { ! 214: while (*body++ == ' '); ! 215: body--; ! 216: if (body == end) ! 217: return 1; ! 218: while (end[-1] == ' ') end--; ! 219: } ! 220: while (body != end) ! 221: h = (h << 5) + h + *body++; ! 222: if (h) ! 223: return h; ! 224: return 1; ! 225: } ! 226: ! 227: /* Return number of characters in display_line p, ! 228: except don't count leading and trailing spaces ! 229: unless the terminal requires those to be explicitly output. */ ! 230: ! 231: line_draw_cost (p) ! 232: struct display_line *p; ! 233: { ! 234: register char *body; ! 235: register int i; ! 236: ! 237: if (!p) ! 238: return 0; ! 239: ! 240: if (must_write_spaces) ! 241: return p->length; ! 242: ! 243: body = p->body - 1; ! 244: for (i = p->length; i > 0 && body[i - 1] == ' '; i--); ! 245: ! 246: i -= count_blanks (p->body); ! 247: return max (i, 0); ! 248: } ! 249: ! 250: /* The functions on this page are the interface from xdisp.c to redisplay. ! 251: They take cursor position arguments in origin 0. ! 252: ! 253: The only other interface into redisplay is through setting ! 254: cursX and cursY (in xdisp.c) and setting screen_garbaged. */ ! 255: ! 256: /* cancel_line eliminates any request to display a line at position `vpos' */ ! 257: ! 258: cancel_line (vpos) ! 259: int vpos; ! 260: { ! 261: return_display_line (DesiredScreen[vpos + 1]); ! 262: DesiredScreen[vpos + 1] = 0; ! 263: } ! 264: ! 265: /* Get a display_line for displaying on line `vpos' ! 266: and set it up for outputting starting at `hpos' within it. */ ! 267: ! 268: struct display_line * ! 269: get_display_line (vpos, hpos) ! 270: int vpos; ! 271: register int hpos; ! 272: { ! 273: register struct display_line *line; ! 274: register char *p; ! 275: ! 276: if (vpos < 0) abort (); ! 277: ! 278: line = DesiredScreen[vpos + 1]; ! 279: if (line && line->length > hpos) ! 280: abort (); ! 281: if (!line) ! 282: line = new_display_line (); ! 283: ! 284: if (hpos > line->length) ! 285: { ! 286: p = line->body + line->length; ! 287: hpos -= line->length; ! 288: line->length += hpos; ! 289: while (--hpos >= 0) ! 290: *p++ = ' '; ! 291: } ! 292: ! 293: DesiredScreen[vpos + 1] = line; ! 294: ! 295: return line; ! 296: } ! 297: ! 298: /* Scroll lines from vpos `from' up to but not including vpos `end' ! 299: down by `amount' lines (`amount' may be negative). ! 300: Returns nonzero if done, zero if terminal cannot scroll them. */ ! 301: ! 302: int ! 303: scroll_screen_lines (from, end, amount) ! 304: int from, end, amount; ! 305: { ! 306: register int i; ! 307: ! 308: if (!line_ins_del_ok) ! 309: return 0; ! 310: ! 311: if (amount == 0) ! 312: return 1; ! 313: if (amount > 0) ! 314: { ! 315: set_terminal_window (end + amount); ! 316: if (!scroll_region_ok) ! 317: ins_del_lines (end, -amount); ! 318: ins_del_lines (from, amount); ! 319: set_terminal_window (0); ! 320: ! 321: for (i = end + amount; i >= end + 1; i--) ! 322: return_display_line (PhysScreen[i]); ! 323: for (i = end; i >= from + 1; i--) ! 324: PhysScreen[i + amount] = PhysScreen[i]; ! 325: for (i = from + amount; i >= from + 1; i--) ! 326: PhysScreen[i] = 0; ! 327: } ! 328: if (amount < 0) ! 329: { ! 330: set_terminal_window (end); ! 331: ins_del_lines (from + amount, amount); ! 332: if (!scroll_region_ok) ! 333: ins_del_lines (end + amount, -amount); ! 334: set_terminal_window (0); ! 335: ! 336: for (i = from + amount + 1; i <= from; i++) ! 337: return_display_line (PhysScreen[i]); ! 338: for (i = from + 1; i <= end ; i++) ! 339: PhysScreen[i + amount] = PhysScreen[i]; ! 340: for (i = end + amount + 1; i <= end; i++) ! 341: PhysScreen[i] = 0; ! 342: } ! 343: return 1; ! 344: } ! 345: ! 346: /* After updating a window w that isn't the full screen wide, ! 347: copy all the columns that w does not occupy ! 348: into the DesiredScreen lines from the PhysScreen lines ! 349: so that update_screen will not change those columns. */ ! 350: ! 351: preserve_other_columns (w) ! 352: struct window *w; ! 353: { ! 354: register int vpos; ! 355: register struct display_line *l1, *l2; ! 356: int start = XFASTINT (w->left); ! 357: int end = XFASTINT (w->left) + XFASTINT (w->width); ! 358: int bot = XFASTINT (w->top) + XFASTINT (w->height); ! 359: ! 360: for (vpos = XFASTINT (w->top); vpos < bot; vpos++) ! 361: { ! 362: if ((l1 = DesiredScreen[vpos + 1]) ! 363: && (l2 = PhysScreen[vpos + 1])) ! 364: { ! 365: if (start > 0) ! 366: { ! 367: bcopy (l2->body, l1->body, start); ! 368: if (l1->length < start && l1->length < l2->length) ! 369: l1->length = min (start, l2->length); ! 370: } ! 371: if (l2->length > end && l1->length < l2->length) ! 372: { ! 373: while (l1->length < end) ! 374: l1->body[l1->length++] = ' '; ! 375: bcopy (l2->body + end, l1->body + end, l2->length - end); ! 376: l1->length = l2->length; ! 377: } ! 378: } ! 379: } ! 380: } ! 381: ! 382: #ifdef NOTDEF ! 383: ! 384: /* If window w does not need to be updated and isn't the full screen wide, ! 385: copy all the columns that w does occupy ! 386: into the DesiredScreen lines from the PhysScreen lines ! 387: so that update_screen will not change those columns. ! 388: ! 389: Have not been able to figure out how to use this correctly. */ ! 390: ! 391: preserve_my_columns (w) ! 392: struct window *w; ! 393: { ! 394: register int vpos, fin; ! 395: register struct display_line *l1, *l2; ! 396: int start = XFASTINT (w->left); ! 397: int end = XFASTINT (w->left) + XFASTINT (w->width); ! 398: int bot = XFASTINT (w->top) + XFASTINT (w->height); ! 399: ! 400: for (vpos = XFASTINT (w->top); vpos < bot; vpos++) ! 401: { ! 402: if ((l1 = DesiredScreen[vpos + 1]) ! 403: && (l2 = PhysScreen[vpos + 1])) ! 404: { ! 405: if (l2->length > start && l1->length < l2->length) ! 406: { ! 407: fin = l2->length; ! 408: if (fin > end) fin = end; ! 409: while (l1->length < start) ! 410: l1->body[l1->length++] = ' '; ! 411: bcopy (l2->body + start, l1->body + start, fin - start); ! 412: l1->length = fin; ! 413: } ! 414: } ! 415: } ! 416: } ! 417: ! 418: #endif /* NOTDEF */ ! 419: ! 420: /* On discovering that the redisplay for a window was no good, ! 421: cancel the columns of that window, ! 422: so that when the window is displayed over again ! 423: get_display_line will not complain. */ ! 424: ! 425: cancel_my_columns (w) ! 426: struct window *w; ! 427: { ! 428: register int vpos; ! 429: register struct display_line *l; ! 430: register int start = XFASTINT (w->left); ! 431: register int bot = XFASTINT (w->top) + XFASTINT (w->height); ! 432: ! 433: for (vpos = XFASTINT (w->top); vpos < bot; vpos++) ! 434: { ! 435: if ((l = DesiredScreen[vpos + 1]) ! 436: && l->length >= start) ! 437: l->length = start; ! 438: } ! 439: } ! 440: ! 441: direct_output_for_insert (c) ! 442: int c; ! 443: { ! 444: register struct display_line *p = PhysScreen[cursY + 1]; ! 445: #ifndef COMPILER_REGISTER_BUG ! 446: register ! 447: #endif COMPILER_REGISTER_BUG ! 448: struct window *w = XWINDOW (selected_window); ! 449: #ifndef COMPILER_REGISTER_BUG ! 450: register ! 451: #endif COMPILER_REGISTER_BUG ! 452: int hpos = cursX; ! 453: ! 454: /* Give up if about to continue line */ ! 455: if (hpos - XFASTINT (w->left) + 1 + 1 >= XFASTINT (w->width)) ! 456: return; ! 457: ! 458: /* Avoid losing if cursor is in invisible text off left margin */ ! 459: if (XINT (w->hscroll) && hpos == XFASTINT (w->left)) ! 460: return; ! 461: ! 462: /* Give up if cursor outside window (in minibuf, probably) */ ! 463: if (cursY < XFASTINT (w->top) ! 464: || cursY >= XFASTINT (w->top) + XFASTINT (w->height)) ! 465: return; ! 466: ! 467: /* Give up if cursor not really at cursX, cursY */ ! 468: if (!display_completed) ! 469: return; ! 470: ! 471: /* Give up if w is minibuffer and a message is being displayed there */ ! 472: if (EQ (selected_window, minibuf_window) && minibuf_message) ! 473: return; ! 474: ! 475: p->body[hpos] = c; ! 476: unchanged_modified = bf_modified; ! 477: beg_unchanged = bf_s1; ! 478: XFASTINT (w->last_point) = point; ! 479: XFASTINT (w->last_point_x) = cursX; ! 480: XFASTINT (w->last_modified) = bf_modified; ! 481: ! 482: reassert_line_highlight (0, cursY); ! 483: write_chars (p->body + hpos, 1); ! 484: fflush (stdout); ! 485: ++cursX; ! 486: p->length = max (p->length, cursX); ! 487: p->body[p->length] = 0; ! 488: } ! 489: ! 490: direct_output_forward_char (n) ! 491: int n; ! 492: { ! 493: register struct window *w = XWINDOW (selected_window); ! 494: ! 495: /* Avoid losing if cursor is in invisible text off left margin */ ! 496: if (XINT (w->hscroll) && cursX == XFASTINT (w->left)) ! 497: return; ! 498: ! 499: cursX += n; ! 500: XFASTINT (w->last_point_x) = cursX; ! 501: XFASTINT (w->last_point) = point; ! 502: topos (cursY, cursX); ! 503: fflush (stdout); ! 504: } ! 505: ! 506: /* At the time this function is called, ! 507: no line is common to PhysScreen and DesiredScreen. ! 508: That is true again when this function returns. */ ! 509: ! 510: /* `force' nonzero means do not stop for pending input */ ! 511: ! 512: /* Value is nonzero if redisplay stopped due to pending input */ ! 513: update_screen (force, inhibit_hairy_id) ! 514: int force; ! 515: int inhibit_hairy_id; ! 516: { ! 517: register struct display_line **p; ! 518: register struct display_line *l, *lnew; ! 519: register int i; ! 520: int pause; ! 521: int preempt_count; ! 522: int outq; ! 523: extern input_pending; ! 524: ! 525: if (screen_height == 0) abort (); /* Some bug zeros some core */ ! 526: ! 527: bcopy (PhysScreen, OPhysScreen, sizeof PhysScreen); ! 528: ! 529: detect_input_pending (); ! 530: if (input_pending && !force) ! 531: { ! 532: pause = 1; ! 533: goto do_pause; ! 534: } ! 535: ! 536: update_begin (); ! 537: ! 538: if (!line_ins_del_ok) ! 539: inhibit_hairy_id = 1; ! 540: ! 541: /* Don't compute for i/d line if just want cursor motion. */ ! 542: for (p = &DesiredScreen[screen_height]; p != DesiredScreen && *p == 0; p--); ! 543: ! 544: /* Try doing i/d line, if not yet inhibited. */ ! 545: if (!inhibit_hairy_id && p != DesiredScreen) ! 546: force |= scrolling (); ! 547: ! 548: /* Update the individual lines as needed. Do bottom line first. */ ! 549: ! 550: l = DesiredScreen[screen_height]; ! 551: if (l && l != PhysScreen[screen_height]) ! 552: update_line (PhysScreen[screen_height], l, screen_height - 1); ! 553: preempt_count = baud_rate / 2400; ! 554: for (i = 1; i < screen_height && (force || !input_pending); i++) ! 555: { ! 556: l = PhysScreen[i]; ! 557: lnew = DesiredScreen[i]; ! 558: if (lnew && lnew != l) ! 559: { ! 560: /* Flush out every so many lines. ! 561: Also flush out if likely to have more than 1k buffered otherwise. ! 562: I'm told that telnet connections get really screwed by more ! 563: than 1k output at once. */ ! 564: outq = stdout->_ptr - stdout->_base; ! 565: if (outq > ((--preempt_count < 0) ? 20 : 900)) ! 566: { ! 567: fflush (stdout); ! 568: if (baud_rate < 2400) ! 569: { ! 570: #ifdef TIOCOUTQ ! 571: if (ioctl (0, TIOCOUTQ, &outq) < 0) ! 572: /* Probably not a tty. Ignore the error and reset ! 573: * the outq count. */ ! 574: outq = stdout->_ptr - stdout->_base; ! 575: #endif ! 576: outq *= 10; ! 577: outq /= baud_rate; /* outq is now in seconds */ ! 578: if (outq) ! 579: sleep (outq); ! 580: } ! 581: detect_input_pending (); ! 582: ! 583: preempt_count = baud_rate / 2400; ! 584: } ! 585: /* Now update this line. */ ! 586: update_line (l, lnew, i - 1); ! 587: } ! 588: } ! 589: pause = (i < screen_height) ? i : 0; ! 590: ! 591: /* Now just clean up termcap drivers and set cursor, etc. */ ! 592: if (!pause) ! 593: topos (cursY, max (min (cursX, screen_width - 1), 0)); ! 594: ! 595: update_end (); ! 596: ! 597: if (termscript) ! 598: fflush (termscript); ! 599: fflush (stdout); ! 600: ! 601: do_pause: ! 602: if (screen_height == 0) abort (); /* Some bug zeros some core */ ! 603: display_completed = !pause; ! 604: /* Free any lines still in desired screen but not in phys screen */ ! 605: /* Free any lines that used to be in phys screen but are no longer */ ! 606: for (p = &PhysScreen[screen_height]; p != PhysScreen; p--) ! 607: if (p[0]) p[0]->physical = 1; ! 608: for (p = &DesiredScreen[screen_height]; p != DesiredScreen; p--) ! 609: { ! 610: if (l = *p) ! 611: { ! 612: if (!l->physical) ! 613: { ! 614: return_display_line (l); ! 615: /* Prevent line in both DesiredScreen and OPhysScreen ! 616: from being freed twice. */ ! 617: l->physical = 1; ! 618: } ! 619: } ! 620: } ! 621: for (p = &OPhysScreen[screen_height]; p != OPhysScreen; p--) ! 622: { ! 623: if (l = *p) ! 624: { ! 625: if (!l->physical) ! 626: return_display_line (l); ! 627: } ! 628: } ! 629: i = 0; ! 630: for (p = &PhysScreen[screen_height]; p != PhysScreen; p--) ! 631: if (p[0]) ! 632: { ! 633: i++; ! 634: p[0]->physical = 0; ! 635: } ! 636: ! 637: { ! 638: extern int debug_end_pos; ! 639: if (debug_end_pos && i + free_line_count != 2 * screen_height) ! 640: abort (); ! 641: } ! 642: ! 643: bzero (OPhysScreen, (screen_height + 1) * sizeof OPhysScreen[0]); ! 644: bzero (DesiredScreen, (screen_height + 1) * sizeof DesiredScreen[0]); ! 645: return pause; ! 646: } ! 647: ! 648: /* Decide what insert/delete line to do, and do it */ ! 649: ! 650: scrolling () ! 651: { ! 652: int unchanged_at_top, unchanged_at_bottom; ! 653: int window_size; ! 654: int changed_lines; ! 655: int *old_hash = (int *) alloca (screen_height * sizeof (int)); ! 656: int *new_hash = (int *) alloca (screen_height * sizeof (int)); ! 657: int *draw_cost = (int *) alloca (screen_height * sizeof (int)); ! 658: register int i; ! 659: int free_at_end_vpos = screen_height; ! 660: ! 661: /* Compute hash codes of all the lines. ! 662: Also calculate number of changed lines, ! 663: number of unchanged lines at the beginning, ! 664: and number of unchanged lines at the end. */ ! 665: ! 666: changed_lines = 0; ! 667: unchanged_at_top = 0; ! 668: unchanged_at_bottom = screen_height; ! 669: for (i = 0; i < screen_height; i++) ! 670: { ! 671: old_hash[i] = line_hash_code (PhysScreen[i + 1]); ! 672: if (!DesiredScreen[i + 1]) ! 673: DesiredScreen[i + 1] = PhysScreen[i + 1]; ! 674: if (PhysScreen[i + 1] == DesiredScreen[i + 1]) ! 675: new_hash[i] = old_hash[i]; ! 676: else ! 677: new_hash[i] = line_hash_code (DesiredScreen[i + 1]); ! 678: if (old_hash[i] != new_hash[i]) ! 679: { ! 680: changed_lines++; ! 681: unchanged_at_bottom = screen_height - i - 1; ! 682: } ! 683: else if (i == unchanged_at_top) ! 684: unchanged_at_top++; ! 685: draw_cost[i] = line_draw_cost (DesiredScreen[i + 1]); ! 686: } ! 687: ! 688: /* If changed lines are few, don't allow preemption, don't scroll. */ ! 689: if (changed_lines < baud_rate / 2400 || unchanged_at_bottom == screen_height) ! 690: return 1; ! 691: ! 692: window_size = screen_height - unchanged_at_top - unchanged_at_bottom; ! 693: ! 694: if (scroll_region_ok) ! 695: free_at_end_vpos -= unchanged_at_bottom; ! 696: else if (memory_below_screen) ! 697: free_at_end_vpos = -1; ! 698: ! 699: /* If large window, fast terminal and few lines in common between ! 700: PhysScreen and DesiredScreen, don't bother with i/d calc. */ ! 701: if (window_size >= 18 && baud_rate > 2400 ! 702: && (window_size >= ! 703: 10 * scrolling_max_lines_saved (unchanged_at_top, ! 704: screen_height - unchanged_at_bottom, ! 705: old_hash, new_hash, draw_cost))) ! 706: return 0; ! 707: ! 708: scrolling_1 (window_size, unchanged_at_top, unchanged_at_bottom, ! 709: draw_cost + unchanged_at_top - 1, ! 710: old_hash + unchanged_at_top - 1, ! 711: new_hash + unchanged_at_top - 1, ! 712: free_at_end_vpos - unchanged_at_top); ! 713: ! 714: return 0; ! 715: } ! 716: ! 717: update_line (old, new, vpos) ! 718: struct display_line *old, *new; ! 719: int vpos; ! 720: { ! 721: register char *obody, *nbody, *op1, *op2, *np1; ! 722: int tem; ! 723: int osp, nsp, m1, m2, olen, nlen; ! 724: int save; ! 725: ! 726: if (old == new) ! 727: return; ! 728: ! 729: /* Mark physical screen as containing the line `new' */ ! 730: PhysScreen[vpos + 1] = new; ! 731: ! 732: if ((new && new->highlighted) != (old && old->highlighted)) ! 733: { ! 734: change_line_highlight (new && new->highlighted, vpos, old ? old->length : 0); ! 735: old = 0; ! 736: } ! 737: else ! 738: reassert_line_highlight (new && new->highlighted, vpos); ! 739: ! 740: if (!old) ! 741: { ! 742: olen = 0; ! 743: } ! 744: else ! 745: { ! 746: obody = old -> body; ! 747: olen = old->length; ! 748: if (!must_write_spaces) ! 749: while (obody[olen - 1] == ' ') ! 750: olen--; ! 751: } ! 752: ! 753: if (!new) ! 754: { ! 755: nlen = 0; ! 756: goto just_erase; ! 757: } ! 758: ! 759: nbody = new -> body; ! 760: nlen = new->length; ! 761: ! 762: /* We know that the previous character is the `physical' field ! 763: and it is zero or one. */ ! 764: if (!must_write_spaces) ! 765: while (nbody[nlen - 1] == ' ') ! 766: nlen--; ! 767: ! 768: if (!olen) ! 769: { ! 770: nsp = (must_write_spaces || new->highlighted) ! 771: ? 0 : count_blanks (nbody); ! 772: if (nlen > nsp) ! 773: { ! 774: topos (vpos, nsp); ! 775: write_chars (nbody + nsp, nlen - nsp); ! 776: } ! 777: return; ! 778: } ! 779: ! 780: obody[olen] = 1; ! 781: save = nbody[nlen]; ! 782: nbody[nlen] = 0; ! 783: ! 784: /* Compute number of leading blanks in old and new contents. */ ! 785: osp = count_blanks (obody); ! 786: if (!new->highlighted) ! 787: nsp = count_blanks (nbody); ! 788: else ! 789: nsp = 0; ! 790: ! 791: /* Compute number of matching chars starting with first nonblank. */ ! 792: m1 = count_match (obody + osp, nbody + nsp); ! 793: ! 794: /* Spaces in new match implicit space past the end of old. */ ! 795: /* This isn't really doing anything; osp should be osp + m1. ! 796: I don't dare fix it now since maybe if it does anything ! 797: it will do something bad. */ ! 798: if (!must_write_spaces && osp == olen) ! 799: { ! 800: np1 = nbody + nsp; ! 801: while (np1[m1] == ' ') ! 802: m1++; ! 803: } ! 804: ! 805: /* Avoid doing insert/delete char ! 806: just cause number of leading spaces differs ! 807: when the following text does not match. */ ! 808: if (m1 == 0 && osp != nsp) ! 809: osp = nsp = min (osp, nsp); ! 810: ! 811: /* Find matching characters at end of line */ ! 812: op1 = obody + olen; ! 813: np1 = nbody + nlen; ! 814: op2 = op1 + m1 - min (olen - osp, nlen - nsp); ! 815: while (op1 > op2 && op1[-1] == np1[-1]) ! 816: { ! 817: op1--; ! 818: np1--; ! 819: } ! 820: m2 = obody + olen - op1; ! 821: ! 822: /* Put correct value back in nbody[nlen]. ! 823: This is important because direct_output_for_insert ! 824: can write into the line at a later point. */ ! 825: nbody[nlen] = save; ! 826: ! 827: /* tem gets the distance to insert or delete. ! 828: m2 is how many characters we save by doing so. ! 829: Is it worth it? */ ! 830: ! 831: tem = (nlen - nsp) - (olen - osp); ! 832: if (m2 && tem && m2 <= DCICcost[tem]) ! 833: m2 = 0; ! 834: ! 835: /* nsp - osp is the distance to insert or delete. ! 836: m1 + m2 is how much we save by doing so. ! 837: Is it worth it? */ ! 838: ! 839: if (m1 + m2 && nsp != osp && m1 + m2 <= DCICcost[nsp - osp]) ! 840: { ! 841: m1 = 0; ! 842: m2 = 0; ! 843: osp = nsp = min (osp, nsp); ! 844: } ! 845: ! 846: /* Now go through the line, inserting, writing and deleting as appropriate. */ ! 847: ! 848: if (osp > nsp) ! 849: { ! 850: topos (vpos, nsp); ! 851: delete_chars (osp - nsp); ! 852: } ! 853: else if (nsp > osp) ! 854: { ! 855: /* If going to delete chars later in line ! 856: and insert earlier in the line, ! 857: must delete first to avoid losing data in the insert */ ! 858: if (m2 && nlen < olen + nsp - osp) ! 859: { ! 860: topos (vpos, nlen - m2 + osp - nsp); ! 861: delete_chars (olen + nsp - osp - nlen); ! 862: olen = nlen - (nsp - osp); ! 863: } ! 864: topos (vpos, osp); ! 865: insert_chars ((char *)0, nsp - osp); ! 866: } ! 867: olen += nsp - osp; ! 868: osp = nsp; ! 869: ! 870: tem = nsp + m1 + m2; ! 871: if (nlen != tem || olen != tem) ! 872: { ! 873: topos (vpos, nsp + m1); ! 874: if (!m2 || nlen == olen) ! 875: { ! 876: /* If new text being written reaches right margin, ! 877: there is no need to do clear-to-eol at the end. ! 878: (and it would not be safe, since cursor is not ! 879: going to be "at the margin" after the text is done) */ ! 880: if (nlen == screen_width) ! 881: olen = 0; ! 882: write_chars (nbody + nsp + m1, nlen - tem); ! 883: #ifdef obsolete ! 884: /* the following code loses disastrously if tem == nlen. ! 885: Rather than trying to fix that case, I am trying the simpler ! 886: solution found above. */ ! 887: /* If the text reaches to the right margin, ! 888: it will lose one way or another (depending on AutoWrap) ! 889: to clear to end of line after outputting all the text. ! 890: So pause with one character to go and clear the line then. */ ! 891: if (nlen == screen_width && fast_clear_end_of_line && olen > nlen) ! 892: { ! 893: /* m2 must be zero, and tem must equal nsp + m1 */ ! 894: write_chars (nbody + tem, nlen - tem - 1); ! 895: clear_end_of_line (olen); ! 896: olen = 0; /* Don't let it be cleared again later */ ! 897: write_chars (nbody + nlen - 1, 1); ! 898: } ! 899: else ! 900: write_chars (nbody + nsp + m1, nlen - tem); ! 901: #endif ! 902: } ! 903: else if (nlen > olen) ! 904: { ! 905: write_chars (nbody + nsp + m1, olen - tem); ! 906: insert_chars (nbody + nsp + m1 + olen - tem, nlen - olen); ! 907: olen = nlen; ! 908: } ! 909: else if (olen > nlen) ! 910: { ! 911: write_chars (nbody + nsp + m1, nlen - tem); ! 912: delete_chars (olen - nlen); ! 913: olen = nlen; ! 914: } ! 915: } ! 916: ! 917: just_erase: ! 918: /* If any unerased characters remain after the new line, erase them. */ ! 919: if (olen > nlen) ! 920: { ! 921: topos (vpos, nlen); ! 922: clear_end_of_line (olen); ! 923: } ! 924: } ! 925: ! 926: count_blanks (str) ! 927: char *str; ! 928: { ! 929: register char *p = str; ! 930: while (*str++ == ' '); ! 931: return str - p - 1; ! 932: } ! 933: ! 934: count_match (str1, str2) ! 935: char *str1, *str2; ! 936: { ! 937: register char *p1 = str1; ! 938: register char *p2 = str2; ! 939: while (*p1++ == *p2++); ! 940: return p1 - str1 - 1; ! 941: } ! 942: ! 943: DEFUN ("open-termscript", Fopen_termscript, Sopen_termscript, ! 944: 1, 1, "FOpen termscript file: ", ! 945: "Start writing all terminal output to FILE as well.") ! 946: (file) ! 947: Lisp_Object file; ! 948: { ! 949: file = Fexpand_file_name (file, Qnil); ! 950: termscript = fopen (XSTRING (file)->data, "w"); ! 951: return Qnil; ! 952: } ! 953: ! 954: DEFUN ("set-screen-height", Fset_screen_height, Sset_screen_height, 1, 1, 0, ! 955: "Set number of lines on screen available for use in windows.") ! 956: (n) ! 957: Lisp_Object n; ! 958: { ! 959: CHECK_NUMBER (n, 0); ! 960: change_screen_size (XINT (n), 0); ! 961: return Qnil; ! 962: } ! 963: ! 964: DEFUN ("set-screen-width", Fset_screen_width, Sset_screen_width, 1, 1, 0, ! 965: "Set number of columns on screen available for display.") ! 966: (n) ! 967: Lisp_Object n; ! 968: { ! 969: CHECK_NUMBER (n, 0); ! 970: change_screen_size (0, XINT (n)); ! 971: return Qnil; ! 972: } ! 973: ! 974: DEFUN ("screen-height", Fscreen_height, Sscreen_height, 0, 0, 0, ! 975: "Return number of lines on screen available for use in windows.") ! 976: () ! 977: { ! 978: return make_number (screen_height); ! 979: } ! 980: ! 981: DEFUN ("screen-width", Fscreen_width, Sscreen_width, 0, 0, 0, ! 982: "Return number of columns on screen available for display.") ! 983: () ! 984: { ! 985: return make_number (screen_width); ! 986: } ! 987: ! 988: /* Change the screen height and/or width. Values may be given as zero to ! 989: indicate no change is to take place. */ ! 990: change_screen_size (newlength, newwidth) ! 991: register int newlength, newwidth; ! 992: { ! 993: if ((newlength == 0 || newlength == screen_height) ! 994: && (newwidth == 0 || newwidth == screen_width)) ! 995: return; ! 996: if (newlength && newlength != screen_height) ! 997: { ! 998: if (newlength > MScreenLength) ! 999: newlength = MScreenLength; ! 1000: set_window_height (XWINDOW (minibuf_window)->prev, newlength - 1, 0); ! 1001: XFASTINT (XWINDOW (minibuf_window)->top) = newlength - 1; ! 1002: set_window_height (minibuf_window, 1, 0); ! 1003: screen_height = newlength; ! 1004: set_terminal_window (0); ! 1005: } ! 1006: if (newwidth && newwidth != screen_width) ! 1007: { ! 1008: if (newwidth > MScreenWidth) ! 1009: newwidth = MScreenWidth; ! 1010: set_window_width (XWINDOW (minibuf_window)->prev, newwidth, 0); ! 1011: set_window_width (minibuf_window, newwidth, 0); ! 1012: screen_width = newwidth; ! 1013: } ! 1014: make_display_lines (); ! 1015: calculate_costs (); ! 1016: DoDsp (1); ! 1017: } ! 1018: ! 1019: DEFSIMPLE ("baud-rate", Fbaud_rate, Sbaud_rate, ! 1020: "Return the output baud rate of the terminal.", ! 1021: Lisp_Int, XSETINT, baud_rate) ! 1022: ! 1023: DEFUN ("send-string-to-terminal", Fsend_string_to_terminal, ! 1024: Ssend_string_to_terminal, 1, 1, 0, ! 1025: "Send STRING to the terminal without alteration.\n\ ! 1026: Control characters in STRING will have terminal-dependent effects.") ! 1027: (str) ! 1028: Lisp_Object str; ! 1029: { ! 1030: CHECK_STRING (str, 0); ! 1031: fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, stdout); ! 1032: fflush (stdout); ! 1033: if (termscript) ! 1034: { ! 1035: fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, termscript); ! 1036: fflush (termscript); ! 1037: } ! 1038: return Qnil; ! 1039: } ! 1040: ! 1041: DEFUN ("ding", Fding, Sding, 0, 0, 0, ! 1042: "Beep, or flash the screen.\n\ ! 1043: Terminates any keyboard macro currently executing.") ! 1044: () ! 1045: { ! 1046: Ding (); ! 1047: return Qnil; ! 1048: } ! 1049: ! 1050: Ding () ! 1051: { ! 1052: if (noninteractive) ! 1053: putchar (07); ! 1054: else if (!INTERACTIVE) /* Stop executing a keyboard macro. */ ! 1055: error ("Keyboard macro terminated by a command ringing the bell"); ! 1056: else ! 1057: ring_bell (); ! 1058: fflush (stdout); ! 1059: } ! 1060: ! 1061: DEFUN ("sleep-for", Fsleep_for, Ssleep_for, 1, 1, 0, ! 1062: "Pause, without updating display, for ARG seconds.") ! 1063: (n) ! 1064: Lisp_Object n; ! 1065: { ! 1066: register int t; ! 1067: #ifndef subprocesses ! 1068: #ifdef HAVE_TIMEVAL ! 1069: struct timeval timeout, end_time, garbage1; ! 1070: #endif /* HAVE_TIMEVAL */ ! 1071: #endif /* no subprocesses */ ! 1072: ! 1073: CHECK_NUMBER (n, 0); ! 1074: t = XINT (n); ! 1075: if (t <= 0) ! 1076: return Qnil; ! 1077: ! 1078: #ifdef subprocesses ! 1079: wait_reading_process_input (t, 0, 0); ! 1080: #else /* No subprocesses */ ! 1081: immediate_quit = 1; ! 1082: QUIT; ! 1083: ! 1084: #if defined(HAVE_SELECT) && defined(HAVE_TIMEVAL) ! 1085: gettimeofday (&end_time, &garbage1); ! 1086: end_time.tv_sec += t; ! 1087: ! 1088: while (1) ! 1089: { ! 1090: gettimeofday (&timeout, &garbage1); ! 1091: timeout.tv_sec = end_time.tv_sec - timeout.tv_sec; ! 1092: timeout.tv_usec = end_time.tv_usec - timeout.tv_usec; ! 1093: if (timeout.tv_usec < 0) ! 1094: timeout.tv_usec += 1000000, ! 1095: timeout.tv_sec--; ! 1096: if (timeout.tv_sec < 0) ! 1097: break; ! 1098: if (!select (1, 0, 0, 0, &timeout)) ! 1099: break; ! 1100: } ! 1101: #else /* not both HAVE_SELECT and HAVE_TIMEVAL */ ! 1102: /* Is it safe to quit out of `sleep'? I'm afraid to trust it. */ ! 1103: sleep (t); ! 1104: #endif /* not both HAVE_SELECT and HAVE_TIMEVAL */ ! 1105: ! 1106: immediate_quit = 0; ! 1107: #endif /* no subprocesses */ ! 1108: return Qnil; ! 1109: } ! 1110: ! 1111: DEFUN ("sit-for", Fsit_for, Ssit_for, 1, 1, 0, ! 1112: "Perform redisplay, then wait for ARG seconds or until input is available") ! 1113: (n) ! 1114: Lisp_Object n; ! 1115: { ! 1116: #ifndef subprocesses ! 1117: #ifdef HAVE_TIMEVAL ! 1118: struct timeval timeout; ! 1119: #else ! 1120: int timeout_sec; ! 1121: #endif ! 1122: int waitchannels; ! 1123: #endif /* no subprocesses */ ! 1124: ! 1125: CHECK_NUMBER (n, 0); ! 1126: ! 1127: if (detect_input_pending ()) ! 1128: return Qnil; ! 1129: ! 1130: DoDsp (1); /* Make the screen correct */ ! 1131: if (XINT (n) <= 0) return Qnil; ! 1132: ! 1133: #ifdef subprocesses ! 1134: #ifdef SIGIO ! 1135: gobble_input (); ! 1136: #endif /* SIGIO */ ! 1137: wait_reading_process_input (XINT (n), 1, 1); ! 1138: #else /* no subprocesses */ ! 1139: immediate_quit = 1; ! 1140: QUIT; ! 1141: ! 1142: waitchannels = 1; ! 1143: #ifndef HAVE_TIMEVAL ! 1144: timeout_sec = XINT (n); ! 1145: select (1, &waitchannels, 0, 0, &timeout_sec); ! 1146: #else /* HAVE_TIMEVAL */ ! 1147: timeout.tv_sec = XINT (n); ! 1148: timeout.tv_usec = 0; ! 1149: select (1, &waitchannels, 0, 0, &timeout); ! 1150: #endif /* HAVE_TIMEVAL */ ! 1151: ! 1152: immediate_quit = 0; ! 1153: #endif /* no subprocesses */ ! 1154: return Qnil; ! 1155: } ! 1156: ! 1157: char *terminal_type; ! 1158: ! 1159: /* Initialization done when Emacs fork is started, before doing stty. */ ! 1160: /* Determine terminal type and set terminal_driver */ ! 1161: /* Then invoke its decoding routine to set up variables ! 1162: in the terminal package */ ! 1163: ! 1164: init_display () ! 1165: { ! 1166: MetaFlag = 0; ! 1167: inverse_video = 0; ! 1168: ! 1169: /* Look at the TERM variable and set terminal_driver. */ ! 1170: ! 1171: terminal_type = (char *) getenv ("TERM"); ! 1172: if (!terminal_type) ! 1173: { ! 1174: fprintf (stderr, "Please set the environment variable TERM; see tset(1).\n"); ! 1175: exit (1); ! 1176: } ! 1177: #ifdef HAVE_X_WINDOWS ! 1178: if (!strncmp (terminal_type, "xterm", 5)) ! 1179: x_term_init (); ! 1180: else ! 1181: #endif /* HAVE_X_WINDOWS */ ! 1182: term_init (terminal_type); ! 1183: ! 1184: make_display_lines (); ! 1185: ! 1186: cursX = 0; /* X and Y coordinates of the cursor */ ! 1187: cursY = 0; /* between updates. */ ! 1188: } ! 1189: ! 1190: syms_of_display () ! 1191: { ! 1192: defsubr (&Sopen_termscript); ! 1193: defsubr (&Sding); ! 1194: defsubr (&Ssit_for); ! 1195: defsubr (&Sscreen_height); ! 1196: defsubr (&Sscreen_width); ! 1197: defsubr (&Sset_screen_height); ! 1198: defsubr (&Sset_screen_width); ! 1199: defsubr (&Ssleep_for); ! 1200: defsubr (&Sbaud_rate); ! 1201: defsubr (&Ssend_string_to_terminal); ! 1202: ! 1203: DefBoolVar ("inverse-video", &inverse_video, ! 1204: "*Non-nil means use inverse-video."); ! 1205: DefBoolVar ("visible-bell", &visible_bell, ! 1206: "*Non-nil means try to flash the screen to represent a bell."); ! 1207: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.